React的状态提升和组合

React的状态提升

通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去

示例:

我们写一个关于热水沸腾的组件,当我们在输入框输入的温度大于100度时,文字会显示热水沸腾。这样有两个输入框分别是摄氏度和华氏度。我们要把他们两个的温度同步。

js 复制代码
// 定义两个温度单位
const scaleNames = {
  c: 'Celsius',//摄氏度
  f: 'Fahrenheit'//华氏度
};

// 摄氏度的转换公式
function toCelsius(fahrenheit) {
  return (fahrenheit - 32) * 5 / 9;
}

// 华氏度的转行公式
function toFahrenheit(celsius) {
  return (celsius * 9 / 5) + 32;
}

// 两个度量单位之间进行转换,使之同步
function tryConvert(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();
}

// 判断是否沸腾
function BoilingVerdict(props) {
  if (props.celsius >= 100) {
    return <p>The water would boil.</p>;
  }
  return <p>The water would not boil.</p>;
}

// 子组件,主要输入框,已经是否沸腾的判断,要求传入scale 、temperature、onTemperatureChange
class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.props.onTemperatureChange(e.target.value);
  }

  render() {
    const temperature = this.props.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}
// 父组件,进行状态提升。同步两个组件的状态,
class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};
  }

  handleCelsiusChange(temperature) {
    this.setState({scale: 'c', temperature});
  }

  handleFahrenheitChange(temperature) {
    this.setState({scale: 'f', temperature});
  }

  render() {
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    // 把华氏度转为摄氏度
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
      // 把摄氏度转为华氏度
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

    return (
      <div>
        <TemperatureInput
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange} />
        <TemperatureInput
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChange} />
        <BoilingVerdict
          celsius={parseFloat(celsius)} />
      </div>
    );
  }
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Calculator />);

让我们来重新梳理一下当你对输入框内容进行编辑时会发生些什么:

  • React 会调用 DOM 中 <input>onChange 方法。在本实例中,它是 TemperatureInput 组件的 handleChange 方法。
  • TemperatureInput 组件中的 handleChange 方法会调用 this.props.onTemperatureChange(),并传入新输入的值作为参数。其 props 诸如 onTemperatureChange 之类,均由父组件 Calculator 提供。
  • 起初渲染时,用于摄氏度输入的子组件 TemperatureInput 中的 onTemperatureChange 方法与 Calculator 组件中的 handleCelsiusChange 方法相同,而,用于华氏度输入的子组件 TemperatureInput 中的 onTemperatureChange 方法与 Calculator 组件中的 handleFahrenheitChange 方法相同。因此,无论哪个输入框被编辑都会调用 Calculator 组件中对应的方法。
  • 在这些方法内部,Calculator 组件通过使用新的输入值与当前输入框对应的温度计量单位来调用 this.setState() 进而请求 React 重新渲染自己本身。
  • React 调用 Calculator 组件的 render 方法得到组件的 UI 呈现。温度转换在这时进行,两个输入框中的数值通过当前输入温度和其计量单位来重新计算获得。
  • React 使用 Calculator 组件提供的新 props 分别调用两个 TemperatureInput 子组件的 render 方法来获取子组件的 UI 呈现。
  • React 调用 BoilingVerdict 组件的 render 方法,并将摄氏温度值以组件 props 方式传入。
  • React DOM 根据输入值匹配水是否沸腾,并将结果更新至 DOM。我们刚刚编辑的输入框接收其当前值,另一个输入框内容更新为转换后的温度值。

得益于每次的更新都经历相同的步骤,两个输入框的内容才能始终保持同步。

小结

  1. 在 React 应用中,任何可变数据应当只有一个相对应的唯一数据源,并且应该遵循自上而下的数据流规则
  2. 如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中

组合

包含关系

通过 JSX 嵌套, 我们可以将任意组件作为子组件传递给它们

子组件

js 复制代码
    function FancyBorder(props) {
     return (
       <div className={'FancyBorder FancyBorder-' + props.color}>
         {props.children}    
         </div>
     );
    }

父组件

js 复制代码
    function WelcomeDialog() {
     return (
       <FancyBorder color="blue">
         <h1 className="Dialog-title"> 
              Welcome      
         </h1>      
         <p className="Dialog-message">       
            Thank you for visiting our spacecraft!     
         </p>  
       </FancyBorder>
     );
    }

jcode

方法二:

子组件

JS 复制代码
    function SplitPane(props) {
      return (
        <div className="SplitPane">
          <div className="SplitPane-left">
            {props.left}      </div>
          <div className="SplitPane-right">
            {props.right}      </div>
        </div>
      );
    }

父组件

js 复制代码
    function App() {
      return (
        <SplitPane
          left={
            <Contacts />      }
          right={
            <Chat />      } />
      );
    }

jcode

相关推荐
weifont3 小时前
聊一聊Electron中Chromium多进程架构
javascript·架构·electron
大得3694 小时前
electron结合vue,直接访问静态文件如何跳转访问路径
javascript·vue.js·electron
水银嘻嘻6 小时前
12 web 自动化之基于关键字+数据驱动-反射自动化框架搭建
运维·前端·自动化
it_remember6 小时前
新建一个reactnative 0.72.0的项目
javascript·react native·react.js
小嘟嚷ovo6 小时前
h5,原生html,echarts关系网实现
前端·html·echarts
十一吖i6 小时前
Vue3项目使用ElDrawer后select方法不生效
前端
只可远观6 小时前
Flutter目录结构介绍、入口、Widget、Center组件、Text组件、MaterialApp组件、Scaffold组件
前端·flutter
周胡杰6 小时前
组件导航 (HMRouter)+flutter项目搭建-混合开发+分栏效果
前端·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
敲代码的小吉米7 小时前
前端上传el-upload、原生input本地文件pdf格式(纯前端预览本地文件不走后端接口)
前端·javascript·pdf·状态模式
是千千千熠啊7 小时前
vue使用Fabric和pdfjs完成合同签章及批注
前端·vue.js