状态提升
在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 的最小集合,其他数据均由它们计算产生。