type
status
date
slug
summary
tags
category
icon
password
核心概念
JSX简介
什么是JSX?
以类似模板语法的方式描述UI的一种语法。它是JS的语法糖,本质上也是JS。可以在其中使用变量或表达式、函数等。
为什么使用JSX?
将标记与逻辑 共同存放在称之为“组件”的松散耦合单元之中,来实现关注点分离 (opens new window)。
React不强制使用JSX,但在 JavaScript 代码中将 JSX 和 UI 放在一起时,会在视觉上有辅助作用。
在 JSX 中嵌入表达式
在 JSX 语法中,你可以在大括号内放置任何有效的 JavaScript 表达式 (opens new window)。
JSX 也是一个表达式
在编译之后,JSX 表达式会被转为普通 JavaScript 函数调用,并且对其取值后得到 JavaScript 对象
可以把JSX当初一个对象来对待,可以使用它赋值给变量、当参数输入函数、返回JSX等。
JSX 特定属性
通过使用引号,来将属性值指定为字符串字面量:
JSX中的属性名使用camelCase(小驼峰)命名。如上面例子中的tabIndex
使用大括号,来在属性值中插入一个 JavaScript 表达式:
使用 JSX 指定子元素
JSX 标签里能够包含很多子元素:
JSX 防止注入攻击
React DOM 在渲染所有输入内容之前,默认会进行转义 (opens new window)。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本) (opens new window)攻击。
JSX 表示对象
Babel 会把 JSX 转译成一个名为
React.createElement()
函数调用。以下两种示例代码完全等效:
元素渲染
元素是构成 React 应用的最小砖块,描述了你在屏幕上想看到的内容。
与浏览器的 DOM 元素不同,React 元素是创建开销极小的普通对象。React DOM 会负责更新 DOM 来与 React 元素保持一致。
将一个元素渲染为 DOM
假设你的 HTML 文件某处有一个
<div>
:根节点,React应用一般只有单一根DOM节点
但一个html页面可以有多个React应用,每个应用对应一个独立根节点。
更新已渲染的元素
React 元素是不可变对象 (opens new window)。一旦被创建,你就无法更改它的子元素或者属性。一个元素就像电影的单帧:它代表了某个特定时刻的 UI。
根据我们已有的知识,更新 UI 唯一的方式是创建一个全新的元素,并将其传入
root.render()
。考虑一个计时器的例子:
注意:在实践中,大多数 React 应用只会调用一次 root.render()。在下一个章节,我们将学习如何将这些代码封装到有状态组件 (opens new window)中。
React 只更新它需要更新的部分
上个例子中,尽管每一秒我们都会新建一个描述整个 UI 树的元素,但React DOM 只会更新实际改变了的内容。
注意:react 18版本 已移除原 ReactDOM.render 方法,以 createRoot 代替。
组件&Props
函数组件与 class 组件
1.函数组件
该函数是一个有效的React组件。接收唯一带有数据的props参数,并返回一个React元素。
2.class组件
使用ES6的class来定义:
以上两个组件是等效的。
函数组件与Class组件在事件处理上的一些区别
- 使用
class组件
时的事件处理:onClick={() => this.props.onClick()}
- 使用
函数组件
时的事件处理:onClick={props.onClick}
(注意两侧都没有括号)。
class组件特点:
- 有组件实例
- 有生命周期
- 有 state 和 setState
函数组件特点:
- 没有组件实例
- 没有生命周期
- 没有 state 和 setState,只能接收 props
- 函数组件是一个纯函数,执行完即销毁,无法存储 state
class 组件存在的问题:
- 大型组件很难拆分和重构,变得难以测试
- 相同业务逻辑分散到各个方法中,可能会变得混乱
- 复用逻辑可能变得复杂,如 HOC 、Render Props
所以 react 中更提倡函数式编程,因为函数更灵活,更易拆分,但函数组件太简单,所以出现了hook,hook就是用来增强函数组件功能的。类组件功能最为完备和强大,某些特殊用途(如错误边界)组件只能写成类式组件。函数组件没有this困扰且代码简洁,大部分的普通组件都可以写成函数组件。
属性展开运算符
如果你已经有了一个 props 对象,你可以使用展开运算符 ...来在 JSX 中传递整个 props 对象。以下两个组件是等价的:
还可以选择只保留当前组件需要接收的 props,并使用展开运算符将其他 props 传递下去。
渲染组件(父子组件传值)
React 元素也可以是用户自定义的组件:
通过属性(attributes)来传递值给子组件:
注意: 组件名称必须以大写字母开头。
React 会将以小写字母开头的组件视为原生 DOM 标签。例如,
<div />
代表 HTML 的 div 标签,而 <Welcome />
则代表一个组件,并且需在作用域内使用 Welcome
。组合组件(复用组件)
通常来说,每个新的 React 应用程序的顶层组件都是 App 组件
提取组件(拆分组件)
参考如下
Comment
组件:该组件由于嵌套的关系,变得难以维护,且很难复用它的各个部分。因此,让我们从中提取一些组件出来。
首先,提取
Avatar
组件:props命名原则
Avatar
不需知道它在 Comment
组件内部是如何渲染的。因此,我们给它的 props 起了一个更通用的名字:user
,而不是 author
。我们建议从组件自身的角度命名 props,而不是依赖于调用组件的上下文命名。
接下来,我们将提取
UserInfo
组件,该组件在用户名旁渲染 Avatar
组件:最终的
Comment
组件:最初看上去,提取组件可能是一件繁重的工作,但是,在大型应用中,构建可复用组件库是完全值得的。
提取原则
- UI中多次被使用的部分
- 一个足够复杂的组件(页面)
Props 的只读性(不能修改Props)
组件无论是使用函数声明还是通过 class 声明 (opens new window),都决不能修改自身的 props。
当然,应用程序的 UI 是动态的,并会伴随着时间的推移而变化。在下一章节 (opens new window)中,我们将介绍一种新的概念,称之为 “state”。在不违反上述规则的情况下,state 允许 React 组件随用户操作、网络响应或者其他变化而动态更改输出内容
State&生命周期
State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件.
State类似于vue中的data选项
生命周期
React16 自上而下地对生命周期做了另一种维度的解读:
- Render阶段:计算一些必要的状态信息。
- Pre-commit阶段:这里指的是“更新真正的 DOM 节点”这个动作。该阶段并没有去更新真实的 DOM,不过 DOM 信息已经是可以读取(包括 ref)。
- Commit 阶段:React 会完成真实 DOM 的更新工作。
与此同时,新的生命周期在流程方面,仍然遵循“挂载”、“更新”、“卸载”这三个广义的划分方式。它们分别对应到:
挂载过程:
- constructor
- getDerivedStateFromProps
- render
- componentDidMount
更新过程:
- getDerivedStateFromProps
- shouldComponentUpdate
- render
- getSnapshotBeforeUpdate
- componentDidUpdate
卸载过程:
- componentWillUnmount
新
.png?table=block&id=1203fb40-f181-801f-a3e0-d5f008f8bdcc&t=1203fb40-f181-801f-a3e0-d5f008f8bdcc&width=658.9921875&cache=v2)
旧
.png?table=block&id=1203fb40-f181-8090-bb40-ef86903649c4&t=1203fb40-f181-8090-bb40-ef86903649c4&width=528&cache=v2)
将函数组件转换成 class 组件
在没有useState钩子函数之前,是通过class组件管理State ?
每次组件更新时
render
方法都会被调用,但只要在相同的 DOM 节点中渲染 <Clock />
,就仅有一个 Clock
组件的 class 实例被创建使用。这就使得我们可以使用如 state 或生命周期方法等很多其他特性。单例模式?
向 class 组件中添加局部的 state
将生命周期方法添加到 Class 中
在具有许多组件的应用程序中,当组件被销毁时释放所占用的资源是非常重要的。
当
Clock
组件第一次被渲染到 DOM 中的时候,就为其设置一个计时器 (opens new window)。这在 React 中被称为“挂载(mount)”。同时,当 DOM 中
Clock
组件被删除的时候,应该清除计时器 (opens new window)。这在 React 中被称为“卸载(unmount)”。正确地使用 State
关于
setState()
你应该了解三件事:不要直接修改 State
构造函数是唯一可以给
this.state
直接赋值的地方setState是React内部方法,使state更新具有响应式?
这是框架设计之初刻意为之,react通过迫使你调用setState去更新视图是为了让你意识到你正在改变组件的状态。
这样可以让开发者在潜意识中认为改变state是一件严肃的事情,一定程度上规范开发行为。
以上是setState为什么存在,下面我们来看它的意义。
1.任务驱动的更新
react实现更新的原理并不是vue那样的双向绑定,
(vue用的是gettet和setter(3.0用的Proxy),在你赋值时它就知道哪个字段变了,于是也就知道该更新哪个组件)
而是在你设置state之后,随之产生一个更新任务,任务的出现触发更新调度。setState的作用正是去创建任务,标记哪个fiber节点产生了更新,并触发任务调度。如果直接修改,对象变了,但还是同一个对象,react并不知道数据已经变了。2.任务优先级
setState创建更新时要从scheduler中拿到触发本次更新的优先级,将优先级加入到任务中。
setState作为更新的入口,它是任务下发的起点。也不是说直接改变state就完全不能实现上述两点,但直接改变state想必是要有依赖收集的过程,但react中并不存在依赖收集,直接改了就是改了,并没有触发任何更新相关的流程。
State 的更新可能是异步的
出于性能考虑,React 可能会把多个
setState()
调用合并成一个调用。因为
this.props
和 this.state
可能会异步更新,所以你不要依赖他们的值来更新下一个状态。要解决这个问题,可以让
setState()
接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:State 的更新会被合并
当你调用
setState()
的时候,React 会把你提供的对象合并到当前的 state。这里的合并是浅合并,所以
this.setState({comments})
完整保留了 this.state.posts
, 但是完全替换了 this.state.comments
。数据是向下流动的(单向数据流)
不管是父组件或是子组件都无法知道某个组件是有状态的还是无状态的,并且它们也并不关心它是函数组件还是 class 组件。
这就是为什么称 state 为局部的或是封装的的原因。除了拥有并设置了它的组件,其他组件都无法访问。
组件可以选择把它的 state 作为 props 向下传递到它的子组件中:
FormattedDate
组件会在其 props 中接收参数 date
,但是组件本身无法知道它是来自于 Clock
的 state,或是 Clock
的 props,还是手动输入的。这通常会被叫做“自上而下”或是“单向”的数据流。任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件。
如果你把一个以组件构成的树想象成一个 props 的数据瀑布的话,那么每一个组件的 state 就像是在任意一点上给瀑布增加额外的水源,但是它只能向下流动。
每个组件都是真正独立的。
事件处理
与传统HTML绑定事件的区别
传统的 HTML:
在 React 中略微不同:
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
- 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
阻止默认事件 (合成事件对象)
在 React 中,你需要这样阻止默认事件:
在这里,
e
是一个合成事件。React 根据 W3C 规范 (opens new window)来定义这些合成事件,所以你不需要担心跨浏览器的兼容性问题。React 事件与原生事件不完全相同。如果想了解更多,请查看 SyntheticEvent
(opens new window)参考指南。绑定事件的方式
使用 React 时,你一般不需要使用
addEventListener
为已创建的 DOM 元素添加监听器。事实上,你只需要在该元素初始渲染的时候添加监听器即可。当你使用 ES6 class (opens new window)语法定义一个组件的时候,通常的做法是将事件处理函数声明为 class 中的方法。例如,下面的
Toggle
组件会渲染一个让用户切换开关状态的按钮:你必须谨慎对待 JSX 回调函数中的
this
,在 JavaScript 中,class 的方法默认不会绑定 (opens new window)this
。如果你忘记绑定 this.handleClick
并把它传入了 onClick
,当你调用这个函数的时候 this
的值为 undefined
。这并不是 React 特有的行为;这其实与 JavaScript 函数工作原理 (opens new window)有关。通常情况下,如果你没有在方法后面添加
()
,例如 onClick={this.handleClick}
,你应该为这个方法绑定 this
。如果觉得使用
bind
很麻烦,这里有两种方式可以解决。如果你正在使用实验性的 public class fields 语法 (opens new window),你可以使用 class fields 正确的绑定回调函数:如果你没有使用 class fields 语法,你可以在回调中使用箭头函数 (opens new window):
此语法问题在于每次渲染
LoggingButton
时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。事件命名规范
在 React 中,有一个命名规范,通常会将代表事件的监听 prop 命名为
on[Event]
,将处理事件的监听方法命名为 handle[Event]
这样的格式。向事件处理程序传递参数
在循环中,通常我们会为事件处理函数传递额外的参数。例如,若
id
是你要删除那一行的 ID,以下两种方式都可以向事件处理函数传递参数:在这两种情况下,React 的事件对象
e
会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind
的方式,事件对象以及更多的参数将会被隐式的进行传递。条件渲染
React 中的条件渲染和 JavaScript 中的一样,使用 JavaScript 运算符
if
(opens new window)或者条件运算符 (opens new window)去创建元素来表现当前的状态,然后让 React 根据它们来更新 UI。观察这两个组件:
再创建一个
Greeting
组件,它会根据用户是否登录来决定显示上面的哪一个组件。元素变量
你可以使用变量来储存元素。 它可以帮助你有条件地渲染组件的一部分,而其他的渲染部分并不会因此而改变。
此方式有些复杂~
与运算符 &&
通过花括号包裹代码,你可以在 JSX 中嵌入表达式 (opens new window)。这也包括 JavaScript 中的逻辑与 (&&) 运算符。它可以很方便地进行元素的条件渲染:
如果条件是
true
,&&
右侧的元素就会被渲染,如果是 false
,React 会忽略并跳过它。请注意,返回 false 的表达式会使
&&
后面的元素被跳过,但会返回 false 表达式。在下面示例中,render 方法的返回值是 <div>0</div>
。三目运算符
可通过三目运算符来条件渲染相应组件。
阻止组件渲染
通过让
render
方法直接返回 null
阻止渲染:在组件的
render
方法中返回 null
并不会影响组件的生命周期。例如,上面这个示例中,componentDidUpdate
依然会被调用。列表&Key
在 React 中,把数组转化为元素 (opens new window)列表的过程是相似的。
渲染多个组件
基础列表组件
将列表组合成一个组件:
key
key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。
一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用数据中的 id 来作为元素的 key
当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key:
如果列表项目的顺序可能会变化,我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。
如果你选择不指定显式的 key 值,那么 React 将默认使用索引用作为列表项目的 key 值
用 key 提取组件 (在map方法内设置key)
元素的 key 只有放在就近的数组上下文中才有意义。
例子:不正确的使用 key 的方式
一个好的经验法则是:在
map()
方法中的元素需要设置 key 属性。key 只是在兄弟节点之间必须唯一
数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值。
不能用key
当props名传给子组件
key 会传递信息给 React ,但不会传递给你的组件。如果你的组件中需要使用
key
属性的值,请用其他属性名显式传递这个值:上面例子中,
Post
组件可以读出 props.id
,但是不能读出 props.key
。在 JSX 中嵌入 map()
JSX 允许在大括号中嵌入任何表达式 (opens new window),所以我们可以内联
map()
返回的结果:这么做有时可以使你的代码更清晰,但有时这种风格也会被滥用。
就像在 JavaScript 中一样,何时需要为了可读性提取出一个变量,这完全取决于你。但请记住,如果一个
map()
嵌套了太多层级,那可能就是你提取组件 (opens new window)的一个好时机。表单
受控组件(双向数据绑定)
在 HTML 中,表单元素(如
<input>
、 <textarea>
和 <select>
)通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()
(opens new window)来更新。我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
例如,如果我们想让前一个示例在提交时打印出名称,我们可以将表单写为受控组件:
类似于vue中的双向绑定,v-model
textarea 标签(使用value定义值)
在 HTML 中,
<textarea>
元素通过其子元素定义其文本:在 React 中,
<textarea>
使用 value
属性赋值。这样,可以使得使用 <textarea>
的表单和使用单行 input 的表单非常类似:注意,
this.state.value
初始化于构造函数中,因此文本区域默认有初值select 标签 (value代替选中属性)
React 在根
select
标签上使用 value
属性代替option
元素上的selected
属性(选中属性)在 HTML 中,
<select>
创建下拉列表标签。例如,如下 HTML 创建了水果相关的下拉列表:请注意,由于
selected
属性的缘故,椰子选项默认被选中。React 并不会使用 selected
属性,而是在根 select
标签上使用 value
属性。这在受控组件中更便捷,因为您只需要在根标签中更新它。例如:select多选
你可以将数组传递到
value
属性中,以支持在 select
标签中选择多个选项:小总结
总的来说,这使得
<input type="text">
, <textarea>
和 <select>
之类的标签都非常相似—它们都接受一个 value
属性,你可以使用它来实现受控组件。文件 input 标签 (非受控组件)
在 HTML 中,
<input type="file">
允许用户从存储设备中选择一个或多个文件,将其上传到服务器,或通过使用 JavaScript 的 File API (opens new window)进行控制。因为它的 value 只读,所以它是 React 中的一个非受控组件。将与其他非受控组件在后续文档中 (opens new window)一起讨论。
处理多个输入
当需要处理多个
input
元素时,我们可以给每个元素添加 name
属性,并让处理函数根据 event.target.name
的值选择要执行的操作。受控输入空值
在受控组件 (opens new window)上指定 value 的 prop 会阻止用户更改输入。如果你指定了
value
,但输入仍可编辑,则可能是你意外地将value
设置为 undefined
或 null
。下面的代码演示了这一点。(输入最初被锁定,但在短时间延迟后变为可编辑。)
受控组件的替代品 (非受控组件)
有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理函数,并通过一个 React 组件传递所有的输入 state。当你将之前的代码库转换为 React 或将 React 应用程序与非 React 库集成时,这可能会令人厌烦。在这些情况下,你可能希望使用非受控组件 (opens new window), 这是实现输入表单的另一种方式。
成熟的解决方案
如果你想寻找包含验证、追踪访问字段以及处理表单提交的完整解决方案,使用 Formik (opens new window)是不错的选择。然而,它也是建立在受控组件和管理 state 的基础之上 —— 所以不要忽视学习它们。
状态提升 (共享状态)
通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。
在 React 中,将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 state。这就是所谓的“状态提升”
两个输入框共享数据的例子:
笔记
- 父组件给所有子组件传入state的值
- 子组件修改值时调用父组件的方法并把值传出
- 父组件接收到值之后修改state
- state被修改之后重新执行render函数,并回到第1步
小结
- 任何可变数据应当只有一个相对应的唯一“数据源”
- 通常,state 都是首先添加到需要渲染数据的组件中去
- 然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中
- 你应当依靠自上而下的数据流 (opens new window),而不是尝试在不同组件间同步 state。
- “存在”于组件中的任何 state,仅有组件自己能够修改它
- 如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中。(如上例中,经过tryConvert方法转换的后的值。)
React开发者工具(debug)
当你在 UI 中发现错误时,可以使用 React 开发者工具 (opens new window)来检查问题组件的 props,并且按照组件树结构逐级向上搜寻,直到定位到负责更新 state 的那个组件。
组合vs继承
React 有十分强大的组合模式。我们推荐使用组合而非继承来实现组件间的代码重用。
包含关系 (组件组合)
有些组件无法提前知晓它们子组件的具体内容。在
Sidebar
(侧边栏)和 Dialog
(对话框)等展现通用容器(box)的组件中特别容易遇到这种情况。props的children属性(类似vue的插槽)
我们建议这些组件使用一个特殊的
children
prop 来将他们的子组件传递到渲染结果中:笔记
类似于vue中的插槽
props传入组件(类似vue命名插槽)
少数情况下,你可能需要在一个组件中预留出几个“洞”。这种情况下,我们可以不使用
children
,而是自行约定:将所需内容传入 props,并使用相应的 prop。<Contacts />
和 <Chat />
之类的 React 元素本质就是对象(object),所以你可以把它们当作 props,像其他数据一样传递。你可以将任何格式的数据作为 props 进行传递。笔记
类似于vue中的命名插槽
特例关系(字符串与组件组合)
有些时候,我们会把一些组件看作是其他组件的特殊实例,比如
WelcomeDialog
可以说是 Dialog
的特殊实例。在 React 中,我们也可以通过组合来实现这一点。“特殊”组件可以通过 props 定制并渲染“一般”组件:
class方式的组件组合
组合也同样适用于以 class 形式定义的组件。
那么继承呢?(不推荐)
在 Facebook,我们在成百上千个组件中使用 React。我们并没有发现需要使用继承来构建组件层次的情况。
Props 和组合为你提供了清晰而安全地定制组件外观和行为的灵活方式。注意:组件可以接受任意 props,包括基本数据类型,React 元素以及函数。
如果你想要在组件间复用非 UI 的功能,我们建议将其提取为一个单独的 JavaScript 模块,如函数、对象或者类。组件可以直接引入(import)而无需通过 extend 继承它们。
React哲学
React 最棒的部分之一是引导我们思考如何构建一个应用。在这篇文档中,我们将会通过 React 构建一个可搜索的产品数据表格来更深刻地领会 React 哲学。
从设计稿开始

第一步:将设计好的 UI 划分为组件层级
首先,你需要在设计稿上用方框圈出每一个组件(包括它们的子组件)
你会看到我们的应用中包含五个组件。我们已经将每个组件展示的数据标注为了斜体。
FilterableProductTable
(橙色): 是整个示例应用的整体
SearchBar
(蓝色): 接受所有的用户输入
ProductTable
(绿色): 展示数据内容并根据用户输入筛选结果
ProductCategoryRow
(天蓝色): 为每一个产品类别展示标题
ProductRow
(红色): 每一行展示一个产品
你可能注意到,
ProductTable
的表头(包含 “Name” 和 “Price” 的那一部分)并未单独成为一个组件。这仅仅是一种偏好选择,如何处理这一问题也一直存在争论。(简单的可以不用单独分一个组件,复杂的作为一个独立的组件就有必要了)组件层级:
FilterableProductTable
SearchBar
ProductTable
ProductCategoryRow
ProductRow
第二步:用 React 创建一个静态版本
先用已有的数据模型渲染一个不包含交互功能的 UI。最好将渲染 UI 和添加交互这两个过程分开。
这是因为,编写一个应用的静态版本时,往往要编写大量代码,而不需要考虑太多交互细节;添加交互功能时则要考虑大量细节,而不需要编写太多代码。
在构建应用的静态版本时,我们需要创建一些会重用其他组件的组件,然后通过 props 传入所需的数据。props 是父组件向子组件传递数据的方式。即使你已经熟悉了 state 的概念,也完全不应该使用 state 构建静态版本。state 代表了随时间会产生变化的数据。
你可以自上而下或者自下而上构建应用:自上而下意味着首先编写层级较高的组件(比如
FilterableProductTable
),自下而上意味着从最基本的组件开始编写(比如 ProductRow
)。当你的应用比较简单时,使用自上而下的方式更方便;对于较为大型的项目来说,自下而上地构建,并同时为低层组件编写测试是更加简单的方式。React 单向数据流(也叫单向绑定)的思想使得组件模块化,易于快速开发。
在 React 中,有两类“模型”数据:props 和 state。清楚地理解两者的区别是十分重要的
第三步:确定 UI state 的最小(且完整)表示
只保留应用所需的可变 state 的最小集合,其他数据均由它们计算产生。
可以由state计算产生的数据就没必要另外保存一个state!
对于使用props还是state,通过问自己以下三个问题,你可以逐个检查相应数据是否属于 state:
- 该数据是否是由父组件通过 props 传递而来的?如果是,那它应该不是 state。
- 该数据是否随时间的推移而保持不变?如果是,那它应该也不是 state。
- 你能否根据其他 state 或 props 计算出该数据的值?如果是,那它也不是 state。
非state的数据:
- 由父组件传来的
- 不会随时间推移而改变的
- 能根据其他stete或props计算而来的
第四步:确定 state 放置的位置
我们需要确定哪个组件能够改变这些 state,或者说拥有这些 state。
注意:React 中的数据流是单向的,并顺着组件层级从上往下传递。
对于应用中的每一个 state:
- 找到根据这个 state 进行渲染的所有组件。
- 找到他们的共同所有者(common owner)组件(在组件层级上高于所有需要该 state 的组件)。
- 该共同所有者组件或者比它层级更高的组件应该拥有该 state。
- 如果你找不到一个合适的位置来存放该 state,就可以直接创建一个新的组件来存放该 state,并将这一新组件置于高于共同所有者组件层级的位置
第五步:添加反向数据流
让数据反向传递:处于较低层级的表单组件更新较高层级组件的state
实现方式:
- 父组件有一个能够修改state改变的回调函数,并传递给子组件
- 子组件的input修改值,使用
onChange
事件监听,并执行父组件传入的回调函数。
- 作者:慕雨
- 链接:https://www.axin.work/article/5cc20137-2f93-45e5-a9d5-27389e197ece
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。