状态提升

在React应用中,任何可变数据应当只有一个相对应的唯一的‘数据源’,通常state都是首先添加到需要渲染数据的组件中去,然后,如果其他组件也需要这个state,那么可以将其提升至这些组件的共同父组件中

比如想要实现这么一个例子:想要摄氏温度输入框与华氏度的输入框的数据同步更改,并且当摄氏水温大于100时会给出水会沸腾的提示语,反之则是水不会沸腾

show you the code

在这个例子中,由于摄氏温度输入框与华氏度的输入框需要同步,因此将这两个组件需要共享的state向上移动到其最近的共同父组件中,由于两个 TemperatureInput 组件的 props 均来自共同的父组件 Calculator,因此两个输入框中的内容将始终保持一致。同时如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中,比如这个例子中的celsiusValue 和 fahrenheitValue

function Calculator(){
  const [temperature, setTemperature] = useState<string>('')
  const [scale, setScale] = useState<string>('c')

  const toCelsius = useCallback((fahrenheit)=>{
    return (fahrenheit - 32) * 5 / 9;
  },[])

  const toFahrenheit = useCallback((celsius)=>{
    return (celsius * 9 / 5) + 32;
  },[])

  const tryConvert = useCallback((temperature, convert)=>{
    const input = parseFloat(temperature);
    if (Number.isNaN(input)) {
      return '';
    }
    const output = convert(input);
    const rounded = Math.round(output * 1000) / 1000;
    return rounded.toString();
  },[])

  const getRightTemperature = useCallback(()=>{
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
    return {
      celsius,
      fahrenheit,
    }
  },[temperature,scale])

  const handleCTempChange = useCallback((val)=>{
    setScale('c')
    setTemperature(val)
  },[])

  const handleFTempChange = useCallback((val)=>{
    setScale('f')
    setTemperature(val)
  },[])

  return(
    <>
      <TemperatureInput scale='c' temperature={getRightTemperature().celsius} onTempChange={handleCTempChange}/>
      <TemperatureInput scale='f' temperature={getRightTemperature().fahrenheit} onTempChange={handleFTempChange}/>
      <BoilingVerdict celsius={parseFloat(getRightTemperature().celsius))}/>
    </>
  )
}

interface ITprops{
  scale: 'c' | 'f',
  temperature:string,
  onTempChange:(val:string)=>void
}

function TemperatureInput(props:ITprops){
  const scaleNames = {
    c: 'Celsius',
    f: 'Fahrenheit'
  };
  return(
    <fieldset>
      <legend>Enter temperature in {scaleNames[props.scale]}:</legend>
      <input value={props.temperature} onChange={(e)=>props.onTempChange(e.target.value)}></input>
    </fieldset>
  )
}

interface IBprops{
  celsius:number
}

function BoilingVerdict(props:IBprops){
  if (props.celsius >= 100) {
    return <p>The water would boil.</p>;
  }
  return <p>The water would not boil.</p>;
}

React哲学

组合VS继承:在react中并没有发现需要使用继承来构建组件层次的情况,props和组合提供清晰而安全地定制组件外观和行为的灵活方式,组件可以接受任意props,包括基本数据类型,React元素以及函数,如果想要在组件间复用非UI的功能,可以将其提取伟一个单独的JS模块

如何构建一个应用:
划分组件层级(单一功能原则) --> 创建一个静态页面(自上而下或者自下而上) --> 确定state的最小(且完整)表示(DRY) --> 确定state的位置(是否需要状态提升) --> 添加反向数据流(回调函数传递)

DRY: Don’t Repeat Yourself,只保留应用所需的可变 state 的最小集合,其他数据均由它们计算产生。

03-05 14:03