侧边栏壁纸
博主头像
恪晨博主等级

前端程序员

  • 累计撰写 140 篇文章
  • 累计创建 41 个标签
  • 累计收到 17 条评论

目 录CONTENT

文章目录

React-React状态提升

恪晨
2020-02-11 / 0 评论 / 0 点赞 / 1,863 阅读 / 1 字 / 正在检测是否收录...

状态提升的原因

  在使用多个组件时,需要反应相同的变化数据,这是应该使用状态提升,将共享的状态提升到最近的父组件中。

举例描述

  以计算水在给定温度下是否会沸腾,设置一个基本的计算器程序。

1、设置一个名称为BoilingVerdict的组件,用于判断接收到的温度是否导致水沸腾。当从props中接收的celsius值大于等于100时,表示沸腾。

 function BoilingVerdict(props) {
      if (props.celsius >= 100) {
          return (<p>ok, 水已经沸腾啦!!!</p>);
      }
      return (<p>在等会,水还没开呢...</p>)
 }

2、设置一个名称为TemperatureInput的组件,渲染一个用于输入温度的input,并将值保存到this.state.temperature中,根据scale的值显示摄氏度和华氏度两个输入组件。

 const scaleNames = {
     c: '摄氏度',
     f: '华氏度'
 };
 
 // 将输入温度抽离为一个公告组件
 
 class TemperatureInput extends Component{
     constructor(props) {
         super(props);
         this.state = {
             temperature: ''
         };
         this.handleChangeTemperature = this.handleChangeTemperature.bind(this);
     }
 
     handleChangeTemperature (e) {
         this.setState({
             temperature: e.target.value
         })
     }
 
     render() {
         const temperature = this.state.temperature;
         // 根据传入的scale的值显示不同的提示信息,c显示摄氏度,f显示华氏度
         const scale = this.props.scale;
         return (
             <fieldset>
                 <legend>请输入{scaleNames[scale]}值:</legend>
                 <input
                     type="text"
                     value={temperature}
                     onChange={this.handleChangeTemperature}
                 />
             </fieldset>
         );
     }
 }

3、设置一个名称为Calculator的组件,两个温度输入组件。

 class Calculator extends Component{
     render() {
         return (
             <div>
                 {/*摄氏度输入*/}
                 <TemperatureInput scale="c"/>
                 {/*华氏度输入*/}
                 <TemperatureInput scale="f"/>
             </div>
         );
     }
 }

4、转换函数:将摄氏度转换为华氏度,将华氏度转换为摄氏度进行显示,并进行数据格式转换处理

  // 转换为摄氏度的函数
  function toCelsius(fValue) {
      return (fValue - 32) * 5 / 9;
  }
  // 转换为华氏度的函数
  function toFahrenheit(cValue) {
      return (cValue * 9 / 5) + 32;
  }
  // 根据temperature与转换函数输入计算值函数
  function tryConvert(temperature, convertFun) {
      // 将输入的字符串转换为float类型
      const inputValue = parseFloat(temperature);
      // 如果输入值不合法则返回空
      if (Number.isNaN(inputValue)) {
          return '';
      }
      // 否则通过转换函数进行转换
      const outputValue = convertFun(inputValue);
      // 保留三位小数
      const rounded = Math.round(outputValue * 1000) / 1000;
      // 返回转换后的字符串
      return rounded.toString();
  }

5、状态提升
  目前为止,在Calculator组件中定义的两个组件<TemperatureInput scale="c"/><TemperatureInput scale="f"/>均在各自内部的state中保留着相互独立互不影响的数据,但是我希望能够在输入摄氏温度或者华氏温度时,另外一个也能实时的跟着计算变化。这时就需要使用state提升,将两个组件中共享的state提升到它们最近的父组件中,以此来实现state共享。
  在本示例中,需要将TemperatureInput组件中的state提升到Calculator组件中,以此来实现两个TemperatureInput组件中的state共享。当将state提升到Calculator组件中后,两个TemperatureInput将会拥有相同的数据源,它们的props均是来自于Calculator组件中,因此两个输入框中的内容将会始终保持一致。

  • 修改TemperatureInput组件中的temperature值数据源与handleChangeTemperature函数中的数据更新方式,这样的话,temperature的值将会从Calculator组件中获取,而数据变化时将会触发onTemperatureChange函数,也是在Calculator进行响应的更新,因为二者都是从props中获取到的。
 handleChangeTemperature (e) {
         // 旧
         this.setState({temperature: e.target.value});
         // 新
         this.props.onTemperatureChange(e.target.value);
 }

 render() {
     // 旧
     // const temperature = this.state.temperature;
     // 新
     const temperature = this.props.temperature;
     // 根据传入的scale的值显示不同的提示信息,c显示摄氏度,f显示华氏度
     const scale = this.props.scale;
     return (
         <fieldset>
             <legend>请输入{scaleNames[scale]}值:</legend>
             <input
                 type="text"
                 value={temperature}
                 onChange={this.handleChangeTemperature}
             />
         </fieldset>
     );
 }
  • 修改Calculator组件中的内容,在Calculator组件中的state定义好temperature字段与scale字段,并为两个组件TemperatureInput组件分别这是相关的属性与方法,此时代码如下:
 class Calculator extends Component{
     constructor(props) {
         super(props);
         // 设置一个默认值
         this.state = {
             temperature: '0',
             scale: 'c'
         };
 
         // 绑定change函数
         this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
         this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
     }
 
     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;
 
         // 调用tryConvert函数进行温度值的转换
         // 如果是华氏温度则转换为设施温度
         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>
         );
     }
 }

总结原理

  通过上述方式,就实现了子组件的状态提升,将子组件的state提升到离它们最近的父组件,实现state共享,其中主要原理过程分析如下:

  • React中input变化时会触发onChange事件,在本示例中,当在输入框输入值时会触发TemperatureInput组件上的onTemperatureChange;
  • TemperatureInput组件上的onTemperatureChange触发时,父组件会把this.handleFahrenheitChange或this.handleCelsiusChange传递给子组件;
  • 无论在输入摄氏温度还是华氏温度时,都会触发handleFahrenheitChange或handleCelsiusChange方法,它们都进行setState的操作,因为state状态会发生改变,所以无论在哪个输入框输入值时state都会改变;
  • 因为state的改变,将会重新执行render函数进行DOM渲染,以此来实现页面更新;
  • render重新渲染时,也是触发BoilingVerdict组件,它将接收celsius的值,并进行判断,当celsius的值大于等于100时,则页面提示ok, 水已经沸腾啦!!!,小与100时则提示在等会,水还没开呢...

总结

  使用状态提升,将任何可变的数据都可以集中到一个"数据源",如果其他组件中也需要这个state,则将这个state提升到这些子组件的最近的父组件中,根据自上而下的数据流特性,不仅便于管理数据源,同时在进行排查bug的时也能够有针对性的、集中的进行排查。当然,在react中其实有更好的方式去实现这样的数据管理,如利用redux框架设置store层进行数据管理。因为react是一款用于构建用户界面的javaScript库,所以在使用多数据时需要使用其他的框架,redux将在后面进行介绍。

0
博主关闭了当前页面的评论