• ⑨ 状态提升


    • 多个组件需要反映相同的变化数据,建议将共享状态提升到最近的共同父组件中

    1 创建一个用于计算水在给定温度下是否会沸腾的温度计算器

    1.1 组件 BoilingVerdict 接收 celsius 温度作为一个 porp

    • 并据此打印出 该温度是否足以将水煮沸 的结果
    function BoilingVerdict(props) {
      if(props.celsius >= 100) {
        return <p>The water would boil.</p>
      }
      return <p>The water would not boil.</p>
    }
    

    1.2 组件 Calculator 渲染一个用于输入温度的 input

    class Calculator extends React.Component {
      constructor(props) {
        super(props)
        this.handleChange = this.handleChange.bind(this)
        this.state = { temperature: '' }
      }
      handleChange(e) {
        this.setState({
          temperature: e.target.value
        })
      }
      render() {
        const temperature = this.state.temperature
        return (
          <fieldset>
            <legend>Enter temperature in Celsius:</legend>
            <input value={ temperature } onChange={ this.handleChange } />
            <BoilingVerdict celsius={ parseFloat(temperature) } />
          </fieldset>
        )
      }
    }
    

    2 提供华氏度输入框,保持与摄氏度输入框数据同步

    2.1 先从 Calculator 组件中抽离出 TemperatureInput 组件,然后添加一个新的 prop ,可以是 f or c

    const scaleNames = {
      c: 'Celsius',
      f: 'Fahrenheit'
    }
    class TemperatureInput extends React.Component {
      constructor(props) {
        super(props)
        this.handleChange = this.handleChange.bind(this)
        this.state = { temperature: '' }
      }
      handleChange(e) {
        this.setState({
          temperature: e.target.value
        })
      }
      render() {
        const temperature = this.state.temperature
        const scale = this.props.scale
        return (
          <fieldset>
            <legend>Enter temperature in { scaleNames[scale] }:</legend>
            <input value={ temperature } onChange={ this.handleChange } />
          </fieldset>
        )
      }
    }
    

    2.2 修改Calculator组件,使其渲染两个独立的温度输入框组件

    class Calculator extends React.Component {
      render() {
        <div>
          <TemperatureInput scale="c" />
          <TemperatureInput scale="f" />
        </div>
      }
    } 
    
    此时在其中一个输入框输入温度,另一个并不会更新
    • 因为 Calculator 组件并不知道在 TemperatureInput 组件中的当前温度是多少,所以通过 Calculator 组件并不能展示 BoilingVerdict 组件的渲染结果

    2.3 编写两个可以在华氏度与摄氏度之间相互转换的函数

    function toCelsius(fahrenheit) {
      return (fahrenheit - 32) * 5 / 9
    }
    function toFahrenheit(celsius) {
      return celsius * 9 / 5 + 32
    }
    
    • 当输入temperature的值无效时,函数返回空字符串
    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()
    }
    
    此时两个 TemperatureInput 组件均在各自内部的 state 中相互独立地保存着各自的数据

    2.4 编写函数将两个输入框的值做关联

    状态提升:将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 state
    TemperatureInput 组件中的 state 移动至 Calculator 组件中

    子组件 TemperatureInput 的处理

    ① 我们将 TemperatureInput 组件中的 this.state.temperature 替换为 this.props.temperature

    render() {
      // before: const temperature = this.state.temperature
      const temperature = this.props.temperature
      // ...
    }
    

    ② 当 TemperatureInput 组件想更新温度时,需调用 this.props.onTemperatureChange 来更新它

    handleChange(e) {
      // before: this.setState({ state: e.target.vaule })
      this.props.onTemperatureChange(e.target.vaule)
    }
    

    ③ 当 TemperatureInput 组件想要响应数据改变时,需要调用 Calculator 组件提供的 this.props.onTemperatureChange

    class TemperatureInput extends React.Component {
      constructor(props) {
        super(props)
        this.handleChange = this.handleChange.bind(this)
      }
      handleChange(e) {
        this.props.onTemperatureChange(e.target.vaule)
      }
      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>
        )
      }
    }
    

    父组件需要调用 Calculator 的处理

    • 在摄氏度输入框中键入 37 时,Calculator 组件中的 state 将会是
    {
      temperature: '37',
      scale: 'c'
    }
    
    • 在华氏度的输入框中键入 212 时,Calculator组件中的state将会是
    {
      temperature: '212',
      scale: 'f'
    }
    

    ① 存储最近修改的温度及其计量单位,根据当前的 temperaturescale 就可以计算出另一个输入框的值

    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({
          temperature,
          scale: 'c'
        })
      }
      handleFahrenheitChange(temperature) {
        this.setState({
          temperature,
          scale: 'f'
        })
      }
      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>
            <TemperatureUnput
             scale="c"
             temperature={ celsius }
             onTemperatureChange={ this.handleCelsiusChange }
            >
            <TemperatureUnput
             scale="f"
             temperature={ fahrenheit }
             onTemperatureChange={ this.handleFahrenheitChange }
            >
            <BoilingVerdict celsius={ parseFloat(celsius) } />
          </div>
        )
      }
    }
    

    3 总结

    • 在 React 应用中,任何可变数据应当只有一个相对应的唯一“数据源”。
    1. state 都是首先添加到需要渲染数据的组件中去。
    2. 然后,如果其他组件也需要这个 state ,那么可以将它提升至这些组件的最近共同父组件中。
    应当依靠自上而下的数据流,而不是尝试在不同组件间同步 state
  • 相关阅读:
    axis2调用webservice
    JSON: Property 'xxx' has no getter method的解决办法
    JDK中工具类的使用
    Java权限讲解
    JSON的使用
    策略模式
    Tomcat虚拟目录的设置
    extends 与 implements 的区别
    利用正则表达式分割字符串
    给面板添加背景图片
  • 原文地址:https://www.cnblogs.com/pleaseAnswer/p/15414516.html
Copyright © 2020-2023  润新知