Lifting State Up - 提升状态
经常的,几个组件需要映射相同的数据改变。我们推荐提升共享的state状态到它们最近的公共祖先元素。让我们看看这是如何实现的。
在这个章节,我们将创建一个温度计算器,计算在一个给定的温度,水是否会沸腾。
我们以一个叫做 'BoilingVerdict' 的组件开始。它接收 'celsius' 温度作为一个prop,并且打印是否足够使水非常。

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

接下来,我们创建一个叫做 'Calculator' 的组件。它渲染一个 <input> ,让你输入温度,并使用 'this.state.value' 来保持它的值。
另外,它根据当前输入的值来渲染 'BoilingVerdict' 。

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

添加第二个输入框
我们的新需求是:除了 'Celsius' 输入框,我们提供一个 'Fahrenheit' 输入框,并且它们保持同步。
我们可以通过从 'Calculator' 提取一个 'TemperatureInput' 组件来开始。我们将添加一个新的 'scale' prop给 'TemperatureInput' 组件,'scale' 可以是 '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 = {value: ''};}handleChange(e) {this.setState({value: e.target.value});}render() {const value = this.state.value;const scale = this.props.scale;return (<fieldset><legend>Enter temperature in {scaleNames[scale]}:</legend><input value={value}onChange={this.handleChange} /></fieldset>);  }}


现在让我们来改变 Calculator 来渲染2个独立的温度输入框:
     class Calculator extends React.Component {render() {return (<div><TemperatureInput scale="c" /><TemperatureInput scale="f" /></div>);}}
现在我们已经有2个输入框了,但是当你在它们中的一个输入温度时,另一个不更新。这与我们的需求矛盾:我们想要2者同步。
从 'Calculator' 组件中,我们也无法展示 'BoilingVerdict'。'Calculator' 不知道当前的温度,因为它隐藏在 'TemperatureInput' 的内部。

提升state状态
首先,我们写2个函数,从 'Celsius' 转换为 'Fahrenheit',然后反过来。
     function toCelsius(fahrenheit) {return (fahrenheit - 32) * 5 / 9;}function toFahrenheit(celsius) {return (celsius * 9 / 5) + 32;}
这2个函数转换数字。我们写另外一个函数,接收一个字符串 'value' 和一个转换函数名作为参数,并返回一个字符串。我们使用它来计算一个输入框的值,基于另一个输入框。
当传入一个无效的 'value',返回一个空字符串,并保证输出到第三位小数。
     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();}
例如,tryConvert('abc', toCelsius) 返回一个空字符串,tryConvert('10.22', toFahrenheit)返回 '50.396'
接着,我们从 'TemperatureInput' 组件中,移除 state 状态。
替代的,'TemperatureInput' 组件通过props接收 'value' 和 'onChange' 处理器(之前value是通过 state 来接收):
     class TemperatureInput extends React.Component {constructor(props) {super(props);this.handleChange = this.handleChange.bind(this);}handleChange(e) {this.setState({value: e.target.value});}render() {const value = this.props.value;const scale = this.props.scale;return (<fieldset><legend>Enter temperature in {scaleNames[scale]}:</legend><input value={value}onChange={this.handleChange} /></fieldset>); }}
如果几个组件需要访问同样的state,这是一个信息,state应该被提升到它们最近的公共祖先上来替代。在我们的案例中,公共祖先是 'Calculator'。我们将存储当前的 'value' 和 'scale' 到它的 state。
我们可能已经存在了两个输入框的值,但是它原来是没有必要的。存储最近改变的输入框的值和它代表的 'scale' 就足够了。之后,我们可以根据当前的 'value' 和 'scale' 来推断出另一个输入框的值。
2个输入框保持同步,因为它们的值可以从相同的state来计算的到:
     class Calculator extends React.component {constructor(props) {super(props);this.handleCelsiusChange = this.handleCelsiusChange.bind(this);this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);this.state = {value: '', scale: 'c'};}handleCelsiusChange(value) {this.setState({scale: 'c', value});}handleFahrenheitChange(value) {this.setState({scale: 'f', value});}render() {const scale = this.state.scale;const value = this.state.value;const celsius = scale === 'f' ? tryConvert(value, toCelsius) : value;const fahrenheit = scale === 'c' ? tryConvert(value, toFahrenheit) : value;return (<div><TemperatureInputscale="c"value={celsius}onChange={this.handleCelsiusChange} /><TemperatureInputscale="f"value={fahrenheit}onChange={this.handleFahrenheitChange} /><BoilingVerdictcelsius={parseFloat(celsius)} /></div>);}}
现在,不管你编辑哪个输入框,'Calculator'组件中的 'this.state.value' 和 'this.state.scale' 都会更新。任何用户输入保留下来,一个输入框获取到用户输入到值,另一个输入框基于它,总是重新计算。

课程总结
在React应用中,任何数据都应该有一个单一的真实数据来源。通常的,为了渲染,state是第一个被添加到组件中。之后,如果其他组件也需要它,可以提升state到它们最近的祖先元素上。替代尝试同步不同组件间的state,我们应该依靠的是 'top-down data flow - 自上而下的数据流'
相比于 'two-way binding approaches - 双向绑定方法',提升state状态,涉及写更多的 'boilerplate - 样板文件' 代码,但是它有一个好处,花费更少的工作查找和杜绝bug。因为任何state存在于一些组件,组件单独可以修改它,这样bugs的表面范围就大大减少了。另外,你可以实现任何自定义逻辑来拒绝或转换用户输入。
如果一些变量,通过 'props' 和 'state' 都可以获取,最好不要使用 'state',而应该用 'props'。例如:我们进存储最后编辑的 'value' 和 它的 'scale',而不存储 'celsiusValue' 和 'fahrenheitValue'。在 'rend()' 方法中通过它们,另一个输入框的值总会被计算。这可以让我们清楚或应用于其他字段,而不会丢失用户输入的任何精度
当你在UI中发现一些错误,你可以使用 'React开发者工具' ,来检查 props,并移动树形结构,直到找到组件响应的state更新。这让你可以在源码中追踪bug

React中文文档之Lifting State Up相关推荐

  1. react中文文档、英文文档及JavaScript相关文档及web前端相关资料

    一. react中文文档 https://doc.react-china.org 二. react英文文档 https://reactjs.org 三.react Github https://git ...

  2. React中文文档之State and Lifecycle

    state 和 生命周期 到目前为止,我们仅仅学习了一种方式来更新UI. 我们调用 'ReactDOM.render()' 来改变输出渲染: function tick() {const elemen ...

  3. React中文文档之Thinking in React

    Thinking in React - 思考React 在我们看来,React是使用js来创建大的.速度快的web应用的首选方式.它已经在Facebook和Instagram表现的非常好. React ...

  4. React中文文档之Forms

    Forms - 表单 在React中,HTML表单元素同其他DOM元素,有点不同.因为表单元素天生具备一些内部的state状态.例如:下面的HTML表单接收一个名字: <form>Name ...

  5. React中文文档之Handling Events

    Handling Events - 事件处理 React元素的事件处理同DOM元素的事件处理非常相似. 有一些语法不同: 1.React事件使用 'camelCase-驼峰式' 命名,而不是 'low ...

  6. React中文文档之Conditional Rendering

    Conditional Rendering - 有条件的渲染 在React中,你可以创建唯一的组件,来封装你需要的行为.之后,你可以仅仅渲染它们中的一些,这取决于你应用的状态. React中的有条件的 ...

  7. React中文文档之introducing JSX

    introducing JSX 思考下面的变量声明: const element = <h1>Hello world!</h1>; 这个有趣的标签解析,既不是字符串,也不是HT ...

  8. React中文文档之Rendering Elements

    Rendering Elements - 渲染元素 元素是React应用的最小构建块 一个元素描述了你想要在屏幕上看到的内容: const element = <h1>Hello, wor ...

  9. React中文文档之Components and Props

    Components and Props - 组件和属性 组件允许你分隔UI为独立的.可重用的零件,每个零件是隔离的. 概念上,组件就像js的函数.它们接收任意的输入(被称为 'props'),并返回 ...

最新文章

  1. It is currently in use by another Gradle instance
  2. js填充select下拉框并选择默认值
  3. 一个MySQL锁和面试官大战三十回合,我霸中霸!
  4. ViewPager实现翻页步骤
  5. 《软件建模与设计: UML、用例、模式和软件体系结构》一一2.10 UML扩展机制
  6. sqlserver导出带数据的脚本文件
  7. mysql mydumper_MySQL 之mydumper安装详解
  8. SIGIR2020 | 一种新颖的推荐系统重训练技巧
  9. mysql企业版功能列表_大型企业数据库服务首选,AliSQL这几大企业级功能你了解几个?...
  10. hadoop程序MapReduce之DataSort
  11. python 多关键字排序_用Python排序字​​典
  12. 最新cs1.6服务器ip地址,CS1.6服务器IP地址
  13. VScode配置C/C++环境以及stdafx.h报错解决方法
  14. graphpad两组t检验_Graphpad 分析教程 | 手把手教你玩转独立样本 t 检验
  15. 阻止计算机访问注册表,电脑中毒了,注册表打不开了,提示注册表已被管理员禁止,这该怎么办...
  16. 计算机二级北京工业大学耿丹学院官网,北京工业大学耿丹学院
  17. 每日阅读:你如何过一天,你就如何过一生
  18. 在【此电脑】隐藏【设备和驱动器】中不需要的图标
  19. 企业邮箱群发邮件 - 邮件群组群发邮件详解
  20. QCon旧金山2016大会,议题发布及研讨会一瞥

热门文章

  1. 【笔记】STM32F4xx 时钟定时器
  2. #include中用双引号和用尖括号括起来头文件的区别
  3. 配音秀显示服务器溜去配音啦,配音秀作品恢复方法操作介绍
  4. [读书][笔记]WINDOWS PE权威指南《零》PE基础
  5. OBS录屏如何设置录制窗口大小?
  6. Vue Element正则验证(所有)
  7. 网易我的世界手机版服务器文件在哪个文件夹,网易我的世界手机版怎么导入地图_手机版地图导入教程...
  8. mysql登录框万能密码_Sqli-LABS通关笔录-11[sql注入之万能密码以及登录框报错注入]...
  9. Android仿微信拍摄、录制视频,以及视频播放(基于JCameraView和GSYVideoPlayer)
  10. 关于高版本web3j调用okhttp3.RequestBody.create(Ljava/lang/String;Lokhttp3/MediaType;)Lokhttp3/RequestBody异常