为什么需要状态提升?

有时候,多个组件需要共享状态,此时需要将共享状态提升到最近的共同父组件中去。

首先创建一个判断水是否沸腾的组件BoilingVerdict

celsius 温度作为一个 prop.

function BoilingVerdict(props) {if (props.celsius >= 100) {return <p>The water would boil.</p>;}return <p>The water would not boil.</p>;
}

官方描述:接下来, 我们创建一个名为 Calculator 的组件。它渲染一个用于输入温度的 <input>,并将其值保存在 this.state.temperature 中。另外, 它根据当前输入值渲染 BoilingVerdict 组件。

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><inputvalue={temperature}onChange={this.handleChange} /><BoilingVerdictcelsius={parseFloat(temperature)} /></fieldset>);}
}

添加第二个输入框

在已有摄氏温度输入框的基础上,我们提供华氏度的输入框,并保持两个输入框的数据同步。
先从 Calculator 组件中抽离出 TemperatureInput 组件,然后为其添加一个新的 scale prop,它可以是 “c” 或是 “f”

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>);}
}

修改 Calculator 组件让它渲染两个独立的温度输入框组件:

class Calculator extends React.Component {render() {return (<div><TemperatureInput scale="c" /><TemperatureInput scale="f" /></div>);}
}

解读

我们希望的是当一个输入框的值发生变化的时候,另一个输入框的值也发生变化。即让他们保持同步。

编写转换函数

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

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();
}

举例说明

  • tryConvert(‘abc’, toCelsius) 返回一个空字符串,而 tryConvert(‘10.22’, toFahrenheit) 返回 ‘50.396’。

状态提升

截止到现在,两个 TemperatureInput 组件均在各自内部的 state 中相互独立地保存着各自的数据。

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;// ...

解读

我们要想保持两个输入框中内容的同步,必须将子组件的状态移动到其共同的父组件中。

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

 render() {// Before: const temperature = this.state.temperature;const temperature = this.props.temperature;// ...

解读

由于props是只读的,由于temperature是通过props传递过来的,所以子组件现在失去了对它的控制权。所以此时我们要想继续修改temperature我们需调用this.props.onTemperatureChange 来更新它:

handleChange(e) {// Before: this.setState({temperature: e.target.value});this.props.onTemperatureChange(e.target.value);// ...

官方提示:自定义组件中的 temperature 和 onTemperatureChange 这两个 prop 的命名没有任何特殊含义。我们可以给它们取其它任意的名字,例如,把它们命名为 value 和 onChange 就是一种习惯。

阶段性回顾

我们首先移出了组件自身的state,过使用 this.props.temperature 替代 this.state.temperature 来读取温度数据。当我们想要响应数据改变时,我们需要调用 Calculator 组件提供的 this.props.onTemperatureChange(),而不再使用 this.setState()。

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>);}
}

我们可以存储两个输入框中的值,但这并不是必要的。我们只需要存储最近修改的温度及其计量单位即可,根据当前的 temperature 和 scale 就可以计算出另一个输入框的值。

解读

我们只需要存储最近一次输入的值即可。因为两个输入框中的值来自于同一个state的更新。

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><TemperatureInputscale="c"temperature={celsius}onTemperatureChange={this.handleCelsiusChange} /><TemperatureInputscale="f"temperature={fahrenheit}onTemperatureChange={this.handleFahrenheitChange} /><BoilingVerdictcelsius={parseFloat(celsius)} /></div>);}
}

梳理回顾整个流程

  1. React会调用每一个TemperatureInput中的handleChange方法
  2. 调用的handleChange方法均有父组件提供
  3. 无论哪个输入框的输入发生变化都会调用父组件更新两个输入框中的状态值

为什么任何可变数据应当只有一个相对应的唯一数据源?

带来的好处是,排查和隔离 bug 所需的工作量将会变少。由于“存在”于组件中的任何 state,仅有组件自己能够修改它,因此 bug 的排查范围被大大缩减了。

状态提升(精读React官方文档—10)相关推荐

  1. 无障碍辅助功能(精读React官方文档—13)

    什么是无障碍辅助功能? 官方描述:网络无障碍辅助功能(Accessibility,也被称为 a11y,因为以 A 开头,以 Y 结尾,中间一共 11 个字母)是一种可以帮助所有人获得服务的设计和创造. ...

  2. 阅读React官方文档

    阅读React官方文档 1,组件&props 2,State&生命周期 3,事件处理 (1)事件绑定方法一: (2)事件绑定方法二: (3)事件绑定方法三: (4)向事件传递参数: 4 ...

  3. 【React】React官方文档学习小记

    1.setState可能是异步的 解决:将一个函数传给setState 2.setState是浅合并 3.jsx的事件必须传入的是函数,而不是字符串, onClick必须是小驼峰写法 onClick= ...

  4. React官方文档自学笔记

    一.条件渲染这块的代码: import React from 'react' function UserGreeting(props) {return <h1>Welcome back!& ...

  5. mysql json官方文档,10分钟了解MySQL5.7对原生JSON的支持与用法

    Part1:JSON格式的支持 MySQL5.7版本终于支持了原生的JSON格式,即将关系型数据库和文档型NO_SQL数据库集于一身.本文接下来将对这特性分别就MySQL5.7和MariaDB10.1 ...

  6. React官方文档学习笔记(二)

    深入 JSX 在运行时选择类型 错误 import React from 'react'; import { PhotoStory, VideoStory } from './stories';con ...

  7. React官方文档: 不使用ES6

    一.安装模块 npm install create-react-class --save 二.使用 var createReactClass = require('create-react-class ...

  8. linux3.10.53编译,根据官方文档在Linux下编译安装Apache

    根据官方文档在Linux下编译安装Apache 前言 永远记住官方文档才是最准确的安装手册,这篇文章仅为对官方文档的解读和补充,学习提升务必阅读官方文档: http://httpd.apache.or ...

  9. 再仔细读读react18官方文档吧 20220526

    再仔细读读react18官方文档吧 20220526 首页 什么是react 开始 体验react 在html中如何使用react和jsx? 核心概念 Hello World react18不支持Re ...

最新文章

  1. 2021年大数据常用语言Scala(二十二):函数式编程 映射 map
  2. RichTextBox读写数据库
  3. 安卓 sharedpreferences可以被其它activity读取_【安卓逆向】“一份礼物”之我要o泡逆向分析...
  4. 操作系统原理:全局页面置换算法、工作集页置换、常驻集页置换、抖动问题
  5. ECOMP已经开源成功,这次ATT准备推动白盒路由操作系统DNOS
  6. zebra代码简单分析 --- 001
  7. 如何搭建自己的第四方支付平台?
  8. 上那学计算机,从零开始学电脑(学电脑从哪开始)
  9. 非华为电脑实现多屏协同、一碰传
  10. 旅游B2B2C系统解决方案
  11. mysql不停库全量备份,mysql全量备份数据
  12. python判断_python判断与或
  13. zigbee网关 CC2530 zstack用手机显示终端传来的lm75a温度传感器的值
  14. MVX-Net: Multimodal VoxelNet for 3D Object Detection
  15. 除了压缩包加密,还有这些更好的文件加密方案
  16. FIRST集 FOLLOW集和SELECT集
  17. sql之conver函数的使用
  18. 《计算机网络》——IEEE 802.11 无线局域网、无线局域网的分类、广域网、PPP协议、HDLC协议、链路层设备、冲突域和广播域
  19. python实现离线翻译_基于python实现百度翻译功能
  20. python自动解析json_JsonParser–使用python编写的json解析器

热门文章

  1. 华为OLT常用的查看命令
  2. 分享一下修复项目漏洞步骤
  3. URL结尾不带斜杠NGINX自动301带上斜杠
  4. 编程入门指南——从高中到大学
  5. cam_lidar_calibration标定速腾激光雷达和单目相机外参
  6. 快速排序java简单实现
  7. HTML5期末大作业:网上花店网站设计——响应式的鲜花预订网电商模板(21页) HTML+CSS+JavaScript
  8. Java垃圾回收算法以及垃圾回收器
  9. Linux 基础篇 -- 虚拟机克隆(可以更加方便的创建虚拟机)、虚拟机的迁移和删除
  10. Android实例-OrientationSensor方向传感器(XE8+小米2)