React 进阶 -- 状态提升

如果多个组件需要反映相同的变化数据,可将它们提升至最近的父组件,由父组件渲染后再分发个子组件。

下面是一个汇率转换示例

Money组件渲染出input标签:

import React, { Component } from "react";

const scaleNames = {
  R: "RMB",
  D: "dollar",
};

class Money extends Component {
  handleChange = (e) => {
    this.props.onValueChange(e.target.value);
  };

  render() {
    const value = this.props.value;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>请输入{scaleNames[scale]}:</legend>
        <input value={value} onChange={this.handleChange} />
      </fieldset>
    );
  }
}

export default Money;

Calculator组件我们会把当前输入的 valuescale 保存在组件内部的 state 中。这个 state 就是从两个输入框组件中“提升”而来的,并且它将用作两个输入框组件的共同“数据源”。这是我们为了渲染两个输入框所需要的所有数据的最小表示。

import React, { Component } from "react";
import Money from "./money";
import { toRMB, toDollar, tryConvert } from "./transform";

class Calculator extends Component {
  constructor(props) {
    super(props);
    this.state = { value: 1, scale: "R" };
  }

  handleRMBChange = (value) => {
    this.setState({ scale: "R", value });
  };

  handleDollarChange = (value) => {
    this.setState({ scale: "D", value });
  };

  onValueChange = () => {
    this.setState({
      value: "",
      scale: "",
    });
  };

  render() {
    const scale = this.state.scale;
    const value = this.state.value;
    const RMB = scale === "D" ? tryConvert(value, toRMB) : value;
    const dollar = scale === "R" ? tryConvert(value, toDollar) : value;
    return (
      <div>
        <Money scale="R" value={RMB} onValueChange={this.handleRMBChange} /> //人民币
        <Money
          scale="D"
          value={dollar}
          onValueChange={this.handleDollarChange}
        /> //美元
      </div>
    );
  }
}

export default Calculator;

transform汇率转换组件:

export function toRMB(dollar) {
  return dollar * 7;
}

export function toDollar(RMB) {
  return RMB / 7;
}

export function tryConvert(value, convert) {
  const input = parseFloat(value);
  if (Number.isNaN(input)) {
    return "";
  }
  const output = convert(input);
  const rounded = Math.round(output * 1000) / 1000;
  return rounded.toString();
}

当对输入框内容进行编辑时会发生些什么:

  1. React 会调用 DOM 中 <input>onChange 方法。在本实例中,它是 Money 组件的 handleChange 方法。
  2. Money 组件中的 handleChange 方法会调用 this.props.onValueChange(),并传入新输入的值作为参数。其 props 诸如 onValueChange 之类,均由父组件 Calculator 提供。
  3. 起初渲染时,用于人民币输入的子组件 Money 中的 onValueChange 方法与 Calculator 组件中的 handleRMBChange 方法相同,而,用于美元输入的子组件 Money 中的 onValueChange 方法与 Calculator 组件中的 handleDollarChange 方法相同。因此,无论哪个输入框被编辑都会调用 Calculator 组件中对应的方法。
  4. 在这些方法内部,Calculator 组件通过使用新的输入值与当前输入框对应的货币类型来调用 this.setState() 进而请求 React 重新渲染自己本身。
  5. React 调用 Calculator 组件的 render 方法得到组件的 UI 呈现。汇率转换在这时进行,两个输入框中的数值通过当前输入值和其货币类型来重新计算获得。
  6. React 使用 Calculator 组件提供的新 props 分别调用两个 Money 子组件的 render 方法来获取子组件的 UI 呈现。