译自:https://reactjs.org/docs/lifting-state-up.html (适当进行了裁减)  

  通常我们会碰到这样的情况,当某个组件的state数据改变时,几个React组件同时都需要做出反应。这时我们推荐把相应的state值共享到这些组件最接近的父类中。让我们看下实际是怎么做的。

  在这个章节,我们将创建一个“温度计算器”,这个计算器通过给定的温度来计算水是不是沸腾了。

我们将从一个BoilingVerdice(沸水判定)组件开始。这个组件接受一个摄氏温度值作为其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中。

此外,Calculator组件还将这个温度值传给它的子组件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>
<input
value={temperature}
onChange={this.handleChange} />
<BoilingVerdict
celsius={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>
);
}
}
现在我们有了两个温度输入框,但目前两入输入框不会同步,当我们改动一个输入框的温度值时,另一个输入框的值不会随着改变。这个没有达到我们的需求:我们希望这两个输入框中的值能保持同步变化。
并且在当前这个Calculator组件中我们也无法显示BoilingVerdict组件的内容。因为当前的温度值隐藏在TemperatureInput组件内部了,使得Calculator组件无法获得当前的温度值。
编写转换函数
首先,我们将编写两个函数,将摄氏度转换为华氏温度,然后返回:
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);
//Math.round()是取整四舍五入
const rounded = Math.round(output * 1000) / 1000; return rounded.toString();
}

例如,tryConvert('abc'、to摄氏度)返回一个空字符串,而tryConvert('10.22'、toFahrenheit)返回'50.396'。

提升State

目前,两个TemperatureInput组件都各自保持输入的温度值在自己的local 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;
// ...

但是,我们希望这两个输入是同步的。当我们更新摄氏输入时,华氏输入应反映转换后的温度,反之亦然。

在React中,共享状态通过将需要共享的state值移动到需要它的组件的最近的共同祖先来完成。这被称为“提升State”。我们将state.temperature这个值从TemperatureInput中移除,并将其放入Calculator组件中。

如果Calculator组件拥有了共享状态,它就将成为当前两个温度输入的“真实来源”。这样就可以通过一定的代码引导使得两个温度输入子组件的值保持一致。由于这两个温度输入子组件的props都来自同一个父计算器组件,所以这两个输入将可以做到同步了。

让我们看一下详细的步骤:

我们将把TemperatureInput组件中的this.state.temperature替换为this.props.temperature。现在,让我们假设this.props.temperature已经存在,尽管将来我们需要从Calculator组件上传递它:

render() {
// Before: const temperature = this.state.temperature;
const temperature = this.props.temperature;
// ...
我们知道props是只读的。在TemperatureInput组件中,这个温度值是保存在local state中,所以只能通过调this.setState()来改变它。然而,现在这个温度值是来自父组件的prop,TemperatureInput组件将无法控制这个温度值的修改。
    在React中,通常通过让组件“受控”来解决这个问题。就像DOM <input>同时接受一个值和一个onChange props一样,TemperatureInput组件可以从它的父组件中接受温度值和onTemperatureChange函数做为温度变化的props。

现在,当TemperatureInput组件想要更新它的温度值,只要调this.props.onTemperatureChange就可以了。

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

在深入研究计算器中的更改之前,让我们回顾一下对TemperatureInput组件的更改。this.state.temperature已经从local state中移除,通过this.props.temperature来替代。当我们要进行更改时,我们不再调用this.setState(),而是调用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.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>
);
}
}

现在让我们转向Calculator组件,我们将存储当前输入的温度值和刻度在local state中。这是我们从输入组件中“提升”的state,它将成为两者的“真实之源”。 它是我们需要知道的所有数据的最小表示,以便两个输入组件的呈现,完整的代码如下:

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>;
} 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>
);
}
} ReactDOM.render(
<Calculator />,
document.getElementById('root')
);

界面如下图:

现在就可以达到我们想要的效果了,两个输入框无论改变哪一个,另一个也会随着改变。其中一个温度值是用户的输入,我们会在组件内部保存下来,另一个温度值会通过计算得到。

对于在React应用程序中发生更改的任何数据,都应该有一个“真实来源”。通常,state首先被添加到需要它进行呈现的组件中。然后,如果其他组件也需要它,您可以将它提升到它们最近的共同祖先。与尝试在不同组件之间同步状态不同,您应该依赖自顶向下的数据流。

React组件State提升(译)的更多相关文章

  1. 第二章 设计高质量的React组件

    第二章 设计高质量的React组件 高质量React组件的原则和方法: 划分组件边界的原则: React组件的数据种类: React组件的生命周期. 2.1 易于维护组件的设计要素 1.高内聚:指的是 ...

  2. 28-React state提升、组件组合或继承

    Lifting State Up state提升 对于在React应用程序中更改的任何数据,应该有一个单一的数据源.通常,都是将state添加到需要渲染的组件.如果其他组件也需要它,您可以将其提升到最 ...

  3. 【译】参考手册-React组件

    react version: 15.4.2 React.Component 组件能够让你将UI拆分为多个独立自治并可重用的部分.在 React 中提供了 React.Component. 概述 Rea ...

  4. React组件系统、props与状态(state)

     多个组件合成一个组件: var style = { fontSize: 20, color: '#ff0000' }; var WebSite = React.createClass({ rende ...

  5. 说说React组件的State

    说说React组件的State React的核心思想是组件化的思想,应用由组件搭建而成, 而组件中最重要的概念是State(状态). 正确定义State React把组件看成一个状态机.通过与用户的交 ...

  6. [转] 深入理解React 组件状态(State)

    React 的核心思想是组件化的思想,应用由组件搭建而成,而组件中最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据. 一. 如何定义State 定义一个合适 ...

  7. React组件的State

    React组件的State 1.正确定义State React把组件看成一个状态机.通过与用户的交互,实现不同状态,然后渲染UI,让用户界面和数据保持一致.组件的任何UI改变,都可以从State的变化 ...

  8. 【05】react 之 组件state

    1.1.  状态理解 React的数据流:由父节点传递到子节点(由外到内传递),如果顶层组件某个prop改变了,React会向下传递,重新渲染所有使用过该属性的组件.除此之外React 组件内部还具有 ...

  9. 深入理解React 组件状态(State)

    React 的核心思想是组件化的思想,应用由组件搭建而成,而组件中最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据. 一. 如何定义State 定义一个合适 ...

随机推荐

  1. Linux安装npm并打包前端代码

    查看node版本$ node -v查看npm版本$ npm -v如果没有安装node及npm,需要先安装node及npm#yum install node# yum install npm安装cnpm ...

  2. rsync 文件同步(差异复制)

    Rsync 是一种快速且极其通用的文件复制工具.以其 Delta 传输算法,通过仅发送源文件和目标中现有文件之间的差异来减少通过网络发送的数据量 Rsync 的几种复制方式:Local,SSH 和 R ...

  3. HDU 1035(走迷宫 模拟)

    题意是给定初始位置在一个迷宫中按照要求前进,判断多少步能离开迷宫或者多少步会走入一个长达多少步的循环. 按要求模拟前进的位置,对每一步在 vis[ ] 数组中进行已走步数的记录,走出去或走到已走过的位 ...

  4. HTTP.SYS远程执行代码漏洞分析 (MS15-034 )

    写在前言:   在2015年4月安全补丁日,微软发布了11项安全更新,共修复了包括Microsoft Windows.Internet Explorer.Office..NET Framework.S ...

  5. JAVA方法调用中的解析与分派

    JAVA方法调用中的解析与分派 本文算是<深入理解JVM>的读书笔记,参考书中的相关代码示例,从字节码指令角度看看解析与分派的区别. 方法调用,其实就是要回答一个问题:JVM在执行一个方法 ...

  6. Android手机有的不显示Toast

    解决办法一: 在手机中把该app的通知打开 可以直接设置通知权限:<uses-permission android:name="android.permission.RECEIVE_B ...

  7. 最棒的 JavaScript 学习指南(2018版)

    译者注:原文作者研究了近2.4万篇 JavaScript 文章得出这篇总结,全文包含学习指南.新人上手.Webpack.性能.基础概念.函数式编程.面试.教程案例.Async Await.并发.V8. ...

  8. SCTP一到多式流分回射服程序

    一.服务器程序 #include <stdlib.h> #include <string.h> #include <strings.h> #include < ...

  9. ps 命令的十个简单用法【转】

    注记 ps 命令有两种不同的语法风格 —— BSD 与 UNIX 两种风格.新手常常对这两种形式产生误解,因此我们有必要在这里作一个简单的说明: ps aux 与 ps -aux 是不同的,例如 -u ...

  10. sublime test3 乱码问题的解决

    1.下载ConvertToUTF8插件,地址:http://pan.baidu.com/s/1bnvVd2R 2.按Ctrl+Shift+P打开命令行,输入Install Package,回车,然后继 ...