ReactJS学习笔记系列。

在父子Component之间传递状态

前面说到每个React Component有两个特殊的属性:props和state。前者用来接收外部输入,以便生成相应的React Element;后者是私有的,用来保持内部状态的。

现在有一个问题,如果一个Component依赖于它的子Component的状态的话,该怎么处理?

答案可以参考React的这个例子Lifting State Up。这个例子中实现了一个温度计算器,其中有几个相关的Component:

  • 一个顶层的Component,表示温度计算器
  • 一个子Component,用来表示温度传感器
  • 一个子Component,显示华氏温度
  • 一个子Component,显示摄氏温度

温度传感器采集到的温度,需要传给华氏温度显示器和摄氏温度显示器。这个总协调是在温度计算器中进行的,温度计算器在创立温度传感器这个子Component的时候,在相应的props里面传入一个回调函数。然后,有温度更新的时候,温度传感器通过这个回调函数来通知顶层的温度计算器,接着温度计算器再通知两个温度显示器来更新读数(通过改变温度显示器的输入参数props)。

从上面的例子我们可以看出一些结论:

  • React Component的组织结构是树形的。虽然这棵树上的每个Component的state是私有的,但是每个Component可以修改传递给其子Component的参数props,从而影响子Component的状态。打一个形象的比喻,数据流向瀑布一样,从顶层的Component向下,流向子Component。
  • Props中传递的参数不仅可以是某些值,也可以是一个函数(就像前面例子中的回调函数),甚至可以是一个React Element。

参考: top-down data flow

Controlled Components

在处理HTML的Form元素的时候,React遇到了一点小问题,主要来针对那些<input>或者<textarea>等需要输入的元素,这些元素自己保有内部状态。

举个例子,网页上有一个<input>元素,你在里面输入你的用户名:

  • 当你敲键盘往<input>输入一个字符的时候,这个字符被保存在<input>的内部状态中,并在界面上显示出来
  • 然后浏览器触发一个事件,来通知React这个变化。对于React来说,这个事件其实有点晚了,因为事件已经生效了,React不能通过preventDefault()这个操作来阻止事件的生效。

React的Forms文档中描述了一种办法,来应对上面的情况。这种办法就是让自己成为single source of truth。简单的说,当React收到<input>值变化的消息时,先把值记到自己的状态中,然后用自己状态的中值再次更新这个<input>元素。最终的结果是React Element和<input>的状态获得了同步,副作用是<input>被更新了两次,一次是事件触发前,一次是React收到事件之后。这样看起来<input>被React控制了,那么这个Component可以叫做Controlled Component。

并不是所有的Form元素都要做成Controlled Component,像<input type="file" />这种只读的,可以不用去控制它。React支持uncontrolled components.

参考: Formik是一个第三方库,提供完整的表单处理功能。

(本篇完)