最近又重温复习了一遍 react 文档,收获多多!!!
温故知新,确实 书读百遍,其义自现
语法:大括号
const name = 'Josh Perez';
const element = Hello, {name}
;
const element = ;
在 JSX 语法中,你可以在大括号内放置任何有效的 JavaScript 表达式。
React DOM 使用 camelCase
(小驼峰命名)来定义属性的名称
例如,JSX 里的 class
变成了 className
,而 tabindex
则变为 tabIndex
。
你可以安全地在 JSX 当中插入用户输入内容:
const title = response.potentiallyMaliciousInput;
// 直接使用是安全的:
const element = {title}
;
React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击。
Babel 会把 JSX 转译成一个名为 React.createElement()
函数调用。
React.createElement()
会预先执行一些检查,以帮助你编写无错代码,但实际上它创建了一个这样的对象:
// 注意:这是简化过的结构
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};
大概就是 React.createElemet() 方法会创建一个 具有 type,props,children 等属性的对象
实际上,JSX 仅仅只是 React.createElement(component, props, ...children)
函数的语法糖。
Click Me
会编译为:
React.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'Click Me'
)
如果没有子节点,你还可以使用自闭合的标签形式,如:
会编译为:
React.createElement(
'div',
{className: 'sidebar'}
)
JSX 标签的第一部分指定了 React 元素的类型。
大写字母开头的 JSX 标签意味着它们是 React 组件。这些标签会被编译为对命名变量的直接引用,所以,当你使用 JSX
表达式时,Foo
必须包含在作用域内。
由于 JSX 会编译为 React.createElement
调用形式,所以 React
库也必须包含在 JSX 代码作用域内。
例如,在如下代码中,虽然 React
和 CustomButton
并没有被直接使用,但还是需要导入:
如果你不使用 JavaScript 打包工具而是直接通过 标签加载 React,则必须将
React
挂载到全局变量中。
以小写字母开头的元素代表一个 HTML 内置组件,比如 **你不能将通用表达式作为 React 元素类型。如果你想通过通用表达式来(动态)决定元素类型,你需要首先将它赋值给大写字母开头的变量。**这通常用于根据 prop 来渲染不同组件的情况下: 你可以把包裹在 如果你没给 prop 赋值,它的默认值是 通常,我们不建议不传递 value 给 prop,因为这可能与 ES6 对象简写混淆, 如果你已经有了一个 props 对象,你可以使用展开运算符 你还可以选择只保留当前组件需要接收的 props,并使用展开运算符将其他 props 传递下去。 属性展开在某些情况下很有用,但是也很容易将不必要的 props 传递给不相关的组件,或者将无效的 HTML 属性传递给 DOM。我们建议谨慎的使用该语法。 包含在开始和结束标签之间的 JSX 表达式内容将作为特定属性 值得注意的是有一些 “falsy” 值,如数字 要解决这个问题,确保 反之,如果你想渲染 与浏览器的 DOM 元素不同,React 元素是创建开销极小的普通对象。React DOM 会负责更新 DOM 来与 React 元素保持一致。 仅使用 React 构建的应用通常只有单一的根 DOM 节点。如果你在将 React 集成进一个已有应用,那么你可以在应用中包含任意多的独立根 DOM 节点。 想要将一个 React 元素渲染到根 DOM 节点中,只需把它们一起传入 React 元素可以是 DOM 标签,也可以是用户自定义的组件 React 元素是不可变对象。一旦被创建,你就无法更改它的子元素或者属性。 所以一般是返回新的元素 组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。 有效的 React 组件,因为它接收唯一带有数据的 “props”(代表属性)对象与并返回一个 React 元素。这类组件被称为“函数组件”,因为它本质上就是 JavaScript 函数。 当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。 注意: 组件名称必须以大写字母开头。 **React 会将以小写字母开头的组件视为原生 DOM 标签。**例如, 组件无论是使用函数声明还是通过 class 声明,都绝不能修改自身的 props 这样的函数被称为“纯函数”,因为该函数不会尝试更改入参,且多次调用下相同的入参始终返回相同的结果。 React 非常灵活,但它也有一个严格的规则: 所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。 而state 可以修改内容 state 是私有的,并且完全受控于当前组件 Class 组件应该始终使用 计时器的例子: 关于 例如,此代码不会重新渲染组件: 而是应该使用 构造函数是唯一可以给 出于性能考虑,React 可能会把多个 因为 例如,此代码可能会无法更新计数器: 要解决这个问题,可以让 上面使用了箭头函数,不过使用普通的函数也同样可以: 当你调用 这里的合并是浅合并 state 为局部的或是封装的,除了拥有并设置了它的组件,其他组件都无法访问。 如: 组件可以选择把它的 state 作为 props 向下传递到它的子组件中: 这通常会被叫做“自上而下”或是“单向”的数据流。任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件。 可以在有状态的组件中使用无状态的组件,反之亦然。 例如,传统的 HTML: 在 React 中略微不同: 在 React 中另一个不同点是你不能通过返回 你必须谨慎对待 JSX 回调函数中的 如果不想显示绑定this的话,就使用箭头函数: 在循环中,通常我们会为事件处理函数传递额外的参数。例如,若 在这两种情况下,React 的事件对象 使用 JavaScript 运算符 使用与运算符 && 还可使用三目运算符: key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。 一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用数据中的 id 来作为元素的 key: 如果列表项目的顺序可能会变化,我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。 如果你选择不指定显式的 key 值,那么 React 将默认使用索引用作为列表项目的 key 值。 元素的 key 只有放在就近的数组上下文中才有意义。 比方说,如果你提取出一个 数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值: 对于受控组件来说,输入的值始终由 React 的 state 驱动 总的来说,这使得 在大多数情况下,我们推荐使用 受控组件 来处理表单数据。在一个受控组件中,表单数据是由 React 组件来管理的。另一种替代方案是使用非受控组件,这时表单数据将交由 DOM 节点来处理。 要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以 使用 ref 来从 DOM 节点中获取表单数据。 因为非受控组件将真实数据储存在 DOM 节点中,所以在使用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码。如果你不介意代码美观性,并且希望快速编写代码,使用非受控组件往往可以减少你的代码量。否则,你应该使用受控组件。 在 React 渲染生命周期时,表单元素上的 同样, 在 React 中, 在 React 中,将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 state。这就是所谓的“状态提升”。你应当依靠自上而下的数据流,而不是尝试在不同组件间同步 state。 虽然提升 state 方式比双向绑定方式需要编写更多的“样板”代码,但带来的好处是,排查和隔离 bug 所需的工作量将会变少。由于“存在”于组件中的任何 state,仅有组件自己能够修改它,因此 bug 的排查范围被大大缩减了。 如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中 我们推荐使用组合而非继承来实现组件间的代码重用。 少数情况下,你可能需要在一个组件中预留出几个“洞”。这种情况下,我们可以不使用 (我的理解是:组合就是嵌套的形式) Props 和组合为你提供了清晰而安全地定制组件外观和行为的灵活方式。注意:组件可以接受任意 props,包括基本数据类型,React 元素以及函数。 最容易的方式,是先用已有的数据模型渲染一个不包含交互功能的 UI。最好将渲染 UI 和添加交互这两个过程分开。这是因为,编写一个应用的静态版本时,往往要编写大量代码,而不需要考虑太多交互细节;添加交互功能时则要考虑大量细节,而不需要编写太多代码。所以,将这两个过程分开进行更为合适。 对于应用中的每一个 state: JSX 支持所有 请注意 大多数 React 应用都会使用 Webpack,Rollup 或 Browserify 这类的构建工具来打包文件。打包是一个将文件引入并合并到一个单独文件的过程,最终形成一个 “bundle”。接着在页面上引入该 bundle,整个应用即可一次性加载。 为了避免搞出大体积的代码包,在前期就思考该问题并对代码包进行分割是个不错的选择。 代码分割是由诸如 Webpack,Rollup 和 Browserify(factor-bundle)这类打包器支持的一项技术,能够创建多个包并在运行时动态加载。 对你的应用进行代码分割能够帮助你“懒加载”当前用户所需要的内容,能够显著地提高你的应用性能。尽管并没有减少应用整体的代码体积,但你可以避免加载用户永远不需要的代码,并在初始加载的时候减少所需加载的代码量。 在你的应用中引入代码分割的最佳方式是通过动态 当 Webpack 解析到该语法时,会自动进行代码分割 然后应在 任何组件都可能因渲染而暂停,甚至是已经展示给用户的组件。为了使屏幕内容始终一致,如果一个已经显示的组件暂停,React 必须隐藏它的树,直到最近的 为了使用户体验更好,不再出现闪烁的情况: **特别是在准备新 UI 时,展示 “旧” 的 UI 会体验更好。**你可以尝试使用新的 此处代码会告知 React,**将标签切换为 如果模块加载失败(如网络问题),它会触发一个错误。你可以通过异常捕获边界(Error boundaries)技术来处理这些情况,以显示良好的用户体验并管理恢复事宜。 一个不错的选择是从路由开始。大多数网络用户习惯于页面之间能有个加载切换过程。你也可以选择重新渲染整个页面,这样您的用户就不必在渲染的同时再和页面上的其他元素进行交互。 这里是一个例子,展示如何在你的应用中使用 Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。 Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。 Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据 使用 context, 我们可以避免通过中间元素传递 props: Context 能让你将这些数据向组件树下所有的组件进行“广播”,所有的组件都能访问到这些数据,也能访问到后续的数据更新。 地区偏好 UI 主题 当前认证的用户 首选语言 管理一些缓存数据 (是应用程序中许多组件都需要的属性) Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。 主要是: React.createContext , ThemeContext.Provider (待完善) 会使得组件的复用性变差 如果你只是想避免层层传递一些属性,组件组合(component composition)有时候是一个比 context 更好的解决方案。 这种变化下,只有最顶部的 Page 组件需要知道 但是,这并不适用于每一个场景:这种将逻辑提升到组件树的更高层次来处理,会使得这些高层组件变得更复杂,并且会强行将低层组件适应这样的形式,这可能不会是你想要的。而且你的组件并不限制于接收单个子组件。你可能会传递多个子组件,甚至会为这些子组件(children)封装多个单独的“接口(slots) https://zh-hans.reactjs.org/docs/context.html#when-to-use-context 创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 只有当组件所处的树中没有匹配到 Provider 时,其 每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。 Provider 接收一个 当 Provider 的 通过新旧值检测来确定变化,使用了与 挂载在 class 上的 一个 React 组件可以订阅 context 的变更,此组件可以让你在函数式组件中可以订阅 context。 这种方法需要一个函数作为子元素(function as a child)。这个函数接收当前的 context 值,并返回一个 React 节点。传递给函数的 context 对象接受一个名为 从一个在组件树中嵌套很深的组件中更新 context 是很有必要的。在这种场景下,你可以通过 context 传递一个函数,使得 consumers 组件更新 context: 为了确保 context 快速进行重渲染,React 需要使每一个 consumers 组件的 context 在组件树中成为一个单独的节点。 因为 context 会根据引用标识来决定何时进行渲染(本质上是 为了防止这种情况,将 value 状态提升到父节点的 state 里: 错误边界是一种 React 组件,这种组件可以捕获发生在其子组件树任何位置的 JavaScript 错误,并打印这些错误,同时展示降级 UI,而并不会渲染那些发生崩溃的子组件树。错误边界可以捕获发生在整个子组件树的渲染期间、生命周期方法以及构造函数中的错误。 错误边界无法捕获以下场景中产生的错误: 如果一个 class 组件中定义了 然后你可以将它作为一个常规组件去使用: 错误边界的工作方式类似于 JavaScript 的 注意错误边界仅可以捕获其子组件的错误,它无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会冒泡至最近的上层错误边界,这也类似于 JavaScript 中 错误边界无法捕获事件处理器内部的错误。 如果你需要在事件处理器内部捕获错误,使用普通的 JavaScript Ref 转发是一个可选特性,其允许某些组件接收 在下面的示例中, 这样,使用 注意: 第二个参数 Ref 转发不仅限于 DOM 组件,你也可以转发 refs 到 class 组件实例中。 当 这个技巧对高阶组件(也被称为 HOC)特别有用。让我们从一个输出组件 props 到控制台的 HOC 示例开始: “logProps” HOC 透传(pass through)所有 下面的示例有一点需要注意:refs 将不会透传下去。这是因为 这意味着用于我们 幸运的是,我们可以**使用 React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。 你可以像使用其他任意元素一样使用 https://zh-hans.reactjs.org/docs/fragments.html 具体而言,高阶组件是参数为组件,返回值为新组件的函数。 组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。 之前建议使用 mixins 用于解决横切关注点相关的问题 请注意,HOC 不会修改传入的组件,也不会使用继承来复制其行为。相反,HOC 通过将组件包装在容器组件中来组成新组件。HOC 是纯函数,没有副作用。 被包装组件接收来自容器组件的所有 prop,同时也接收一个新的用于 render 的 与普通组件一样, 不要试图在 HOC 中修改组件原型(或以其他方式改变它)。 这样做会产生一些不良后果。其一是输入组件再也无法像 HOC 增强之前那样使用了。更严重的是,如果你再用另一个同样会修改 修改传入组件的 HOC 是一种糟糕的抽象方式。调用者必须知道他们是如何实现的,以避免与其他 HOC 发生冲突。 HOC 不应该修改传入组件,而应该使用组合的方式,通过将组件包装在容器组件中实现功能: 因为它是一个纯函数,它可以与其他 HOC 组合,甚至可以与其自身组合。 您可能已经注意到 HOC 与容器组件模式之间有相似之处。容器组件担任将高级和低级关注点分离的责任,由容器管理订阅和状态,并将 prop 传递给处理 UI 的组件。HOC 使用容器作为其实现的一部分,你可以将 HOC 视为参数化容器组件。 HOC 为组件添加特性。自身不应该大幅改变约定。HOC 返回的组件与原组件应保持类似的接口。 HOC 应该透传与自身无关的 props 这种约定保证了 HOC 的灵活性以及可复用性。 并不是所有的 HOC 都一样。有时候它仅接受一个参数,也就是被包裹的组件: HOC 通常可以接收多个参数。比如在 Relay 中,HOC 额外接收了一个配置对象用于指定组件的数据依赖: 最常见的 HOC 签名如下: HOC 创建的容器组件会与任何其他组件一样,会显示在 React Developer Tools 中。为了方便调试,请选择一个显示名称,以表明它是 HOC 的产物。 最常见的方式是用 HOC 包住被包装组件的显示名称。比如高阶组件名为 这不仅仅是性能问题 - 重新挂载组件会导致该组件及其所有子组件的状态丢失。 如果在组件之外创建 HOC,这样一来组件只会创建一次。因此,每次 render 时都会是同一个组件。 你将 HOC 应用于组件时,原始组件将使用容器组件进行包装。这意味着新组件没有原始组件的任何静态方法。 但要这样做,你需要知道哪些方法应该被拷贝。你可以使用 hoist-non-react-statics 自动拷贝所有非 React 静态方法: 虽然高阶组件的约定是将所有 props 传递给被包装组件,但这对于 refs 并不适用。那是因为 这个问题的解决方案是通过使用 这段命令将在你的项目下的 正常开发 如果你的应用渲染了长列表(上百甚至上千的数据),我们推荐使用“虚拟滚动”技术。这项技术会在有限的时间内仅渲染有限的内容,并降低重新渲染组件消耗的时间,以及创建 DOM 节点的数量。 react-window 和 react-virtualized 是热门的虚拟滚动库。 当一个组件的 props 或 state 变更,React 会将最新返回的元素与之前渲染的元素进行对比,以此决定是否有必要更新真实的 DOM。当它们不相同时,React 会更新该 DOM。 可以通过覆盖生命周期方法 如果你知道在什么情况下你的组件不需要更新,你可以在 在大部分情况下,你可以继承 在这段代码中, 大部分情况下,你可以使用 避免该问题最简单的方式是不要更改你正用于 props 或 state 的值(不要更正原有的 props和state 的值,而是直接返回新的值) 当处理深层嵌套对象时,以 immutable (不可变)的方式更新 Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。 第一个参数( 通常来讲,当你从组件的 render 方法返回一个元素时,该元素将被挂载到 DOM 节点中离其最近的父节点: 然而,有时候将子元素插入到 DOM 节点中的不同位置也是有好处的: 一个 portal 的典型用例是当父组件有 注意: 当在使用 portal 时, 记住管理键盘焦点就变得尤为重要。 对于模态对话框,通过遵循 WAI-ARIA 模态开发实践,来确保每个人都能够运用它。 一个从 portal 内部触发的事件会一直冒泡至包含 React 树的祖先,即便这些元素并不是 DOM 树 中的祖先。 在父组件里捕获一个来自 portal 冒泡上来的事件,使之能够在开发时具有不完全依赖于 portal 的更为灵活的抽象。例如,如果你在渲染一个 ** 注意: Profiling 增加了额外的开支,所以它在生产构建中会被禁用。 https://zh-hans.reactjs.org/docs/profiler.html 这些方法不会自动绑定 如果使用 ES6 class 关键字创建组件,在处理事件回调时就要多写一部分代码。但对于大型项目来说,这样做可以提升运行效率。 也可以直接将 handleClick 改为 箭头函数,就不需要绑定 this 了 为了安全起见,你可以采用以下几种方式: 如果完全不同的组件有相似的功能,这就会产生“横切关注点(cross-cutting concerns)“问题。 **比较常见的用法是,组件每隔一段时间更新一次。**使用 如果组件拥有多个 mixins,且这些 mixins 中定义了相同的生命周期方法(例如,当组件被销毁时,几个 mixins 都想要进行一些清理工作),那么这些生命周期方法都会被调用的。使用 mixins 时,mixins 会先按照定义时的顺序执行,最后调用组件上对应的方法。 在某一时间节点调用 React 的 此算法有一些通用的解决方案,即生成将一棵树转换成另一棵树的最小操作次数。然而,即使使用最优的算法,该算法的复杂程度仍为 O(n 3 ),其中 n 是树中元素的数量。 React 在以下两个假设的基础之上提出了一套 O(n) 的启发式算法: 当对比两棵树时,React 首先比较两棵树的根节点。不同类型的根节点元素会有不同的形态。 当根节点为不同类型的元素时,React 会拆卸原有的树并且建立起新的树 当卸载一棵树时,对应的 DOM 节点也会被销毁。组件实例将执行 当对比两个相同类型的 React 元素时,React 会保留 DOM 节点,仅比对及更新有改变的属性。 在处理完当前节点之后,React 继续对子节点进行递归。 当一个组件更新时,组件实例会保持不变,因此可以在不同的渲染时保持 state 一致。React 将更新该组件实例的 props 以保证与最新的元素保持一致,并且调用该实例的 下一步,调用 默认情况下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个 mutation。 当子元素拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素。 这个 key 不需要全局唯一,但在列表中需要保持唯一 当基于下标的组件进行重新排序时,组件 state 可能会遇到一些问题。由于组件实例是基于它们的 key 来决定是否更新以及复用,如果 key 是一个下标,那么修改顺序时会修改当前的 key,导致非受控组件的 state(比如输入框)可能相互篡改,会出现无法预期的变动。 在当前的实现中,**可以理解为一棵子树能在其兄弟之间移动,但不能移动到其他位置。**在这种情况下,算法会重新渲染整棵子树。 由于 React 依赖启发式算法,因此当以下假设没有得到满足,性能会有所损耗。 Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。 被修改的子组件可能是一个 React 组件的实例,也可能是一个 DOM 元素。 下面是几个适合使用 refs 的情况: 避免使用 refs 来做任何可以通过声明式实现来完成的事情。 Refs 是使用 当 ref 被传递给 ref 的值根据节点的类型而有所不同: 默认情况下,你不能在函数组件上使用 如果要在函数组件中使用 在函数组件中使用 ref 的话,使用钩子函数 useRef 不管怎样,你可以在函数组件内部使用 在极少数情况下,你可能希望在父组件中引用子节点的 DOM 节点。通常不建议这样做,因为它会打破组件的封装,但它偶尔可用于触发焦点或测量子 DOM 节点的大小或位置。 Ref 转发使组件可以像暴露自己的 ref 一样暴露子组件的 ref。 React 也支持另一种设置 refs 的方式,称为“回调 refs”。它能助你更精细地控制何时 refs 被设置和解除。 不同于传递 具体使用可看这个链接 https://zh-hans.reactjs.org/docs/refs-and-the-dom.html 这也是 render prop 的来历:相比于直接将 提供了一个 render prop 是一个用于告知组件需要渲染什么内容的函数 prop。 这项技术使我们共享行为非常容易。要获得这个行为,只要渲染一个带有 关于 render prop 一个有趣的事情是你可以使用带有 render prop 的常规组件来实现大多数高阶组件 (HOC)。 例如,如果你更喜欢使用 因此,你可以将任一模式与 render prop 一起使用。 重要的是要记住,render prop 是因为模式才被称为 render prop ,你不一定要用名为 如果你在 render 方法里创建函数,那么使用 render prop 会抵消使用 https://zh-hans.reactjs.org/docs/render-props.html 注意: 严格模式检查仅在开发模式下运行;它们不会影响生产构建。 https://zh-hans.reactjs.org/docs/strict-mode.html#identifying-unsafe-lifecycles Flow TypeScript React 内置的 propTypes 出于性能方面的考虑, 以下提供了使用不同验证器的例子: https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html 这篇文章是看react官方文档的重点摘录,供之后便于快速查找重点 会生成相应的字符串
'div'
或者 'span'
传递给 React.createElement
(作为参数)。大写字母开头的元素则对应着在 JavaScript 引入或自定义的组件,如
会编译为 React.createElement(Foo)
。
在运行时选择类型
JSX 中的 Props
{}
中的 JavaScript 表达式作为一个 prop 传递给 JSX 元素Props 默认值为 “True”
true
{foo}
是 {foo: foo}
的简写,而不是 {foo: true}
。这样实现只是为了保持和 HTML 中标签属性的行为一致。属性展开
...
来在 JSX 中传递整个 props 对象。以下两个组件是等价的:kind
的 prop 会被安全的保留,它将不会被传递给 DOM 中的 元素。 所有其他的 props 会通过
...other
对象传递,使得这个组件的应用可以非常灵活。JSX 中的子元素
props.children
传递给外层组件。props.children
和其他 prop 一样,它可以传递任意类型的数据布尔类型、Null 以及 Undefined 将会忽略
false
, null
, undefined
, and true
是合法的子元素。但它们并不会被渲染。以下的 JSX 表达式渲染结果相同:
0
,仍然会被 React 渲染。例如,以下代码并不会像你预期那样工作,因为当 props.messages
是空数组时,将会渲染为数字 0
:&&
之前的表达式总是布尔值:false
、true
、null
、undefined
等值,你需要先将它们转换为字符串:元素
ReactDOM.createRoot()
:更新已渲染的元素
组件 & Props
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Welcome name="Sara" />;
root.render(element);
代表 HTML 的 div 标签,而
则代表一个组件,并且需在作用域内使用 Welcome
。Props 的只读性
State & 生命周期
constructor(props) {
super(props);
this.state = {date: new Date()};
}
props
参数来调用父类的构造函数。正确地使用 State
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);
setState()
你应该了解三件事:不要直接修改 State
// Wrong
this.state.comment = 'Hello';
setState()
:// Correct
this.setState({comment: 'Hello'});
this.state
赋值的地方。State 的更新可能是异步的
setState()
调用合并成一个调用。this.props
和 this.state
可能会异步更新,所以你不要依赖他们的值来更新下一个状态。// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
setState()
接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
State 的更新会被合并
setState()
的时候,React 会把你提供的对象合并到当前的 state。数据是向下流动的
FormattedDate
组件会在其 props 中接收参数 date
,但是组件本身无法知道它是来自于 Clock
的 state,或是 Clock
的 props,还是手动输入的:function FormattedDate(props) {
return
It is {props.date.toLocaleTimeString()}.
;
}
事件处理
<button onclick="activateLasers()">
Activate Lasers
</button>
<button onClick={activateLasers}> Activate Lasers
</button>
false
的方式阻止默认行为。你必须显式地使用 preventDefault
。this
,在 JavaScript 中,class 的方法默认不会绑定 this
。如果你忘记绑定 this.handleClick
并把它传入了 onClick
,当你调用这个函数的时候 this
的值为 undefined
。向事件处理程序传递参数
id
是你要删除那一行的 ID,以下两种方式都可以向事件处理函数传递参数:
e
会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind
的方式,事件对象以及更多的参数将会被隐式的进行传递。条件渲染
if
或者条件运算符function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
Hello!
{unreadMessages.length > 0 &&
You have {unreadMessages.length} unread messages.
}
condition ? true : false
。列表 & Key
key
用 key 提取组件
ListItem
组件,你应该把 key 保留在数组中的这个
元素上,而不是放在 ListItem
组件中的 元素上。
key 值在兄弟节点之间必须唯一
表单
受控组件
,
和
之类的标签都非常相似—它们都接受一个
value
属性,你可以使用它来实现受控组件。非受控组件
默认值
value
将会覆盖 DOM 节点中的值。在非受控组件中,你经常希望 React 能赋予组件一个初始值,但是不去控制后续的更新。 在这种情况下, 你可以指定一个 defaultValue
属性,而不是 value
。在一个组件已经挂载之后去更新 defaultValue
属性的值,不会造成 DOM 上值的任何更新。 和
支持
defaultChecked
, 和
支持
defaultValue
。 始终是一个非受控组件,因为它的值只能由用户设置,而不能通过代码控制。
状态提升
组合 vs 继承
props.children
JSX 标签中的所有内容都会作为一个 children
prop 传递给 FancyBorder
组件。因为 FancyBorder
将 {props.children}
渲染在一个 children
,而是自行约定:将所需内容传入 props,并使用相应的 prop。React 哲学
第一步:将设计好的 UI 划分为组件层级
第二步:用 React 创建一个静态版本
第三步:确定 UI state 的最小(且完整)表示
第四步:确定 state 放置的位置
第五步:添加反向数据流
无障碍辅助功能
aria-*
HTML 属性。虽然大多数 React 的 DOM 变量和属性命名都使用驼峰命名(camelCased),但 aria-* 应该像其在 HTML 中一样使用带连字符的命名法for
在 JSX 中应该被写作 htmlFor
:
透传ref的方式
forwardRef
函数来向被包裹的组件转发 ref。**如果第三方的 HOC 不支持转发 ref,上面的方法仍可以作为一种备选方案。代码分割
打包
import()
import()
语法。React.lazy
const OtherComponent = React.lazy(() => import('./OtherComponent'));
React.lazy
接受一个函数,这个函数需要动态调用 import()
。它必须返回一个 Promise
,该 Promise 需要 resolve 一个 default
export 的 React 组件。Suspense
组件中渲染 lazy 组件,如此使得我们可以使用在等待加载 lazy 组件时做优雅降级(如 loading 指示器等)。import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
fallback
属性接受任何在组件加载过程中你想展示的 React 元素。你可以将 Suspense
组件置于懒加载组件之上的任何位置。你甚至可以用一个 Suspense
组件包裹多个懒加载组件。避免兜底
边界。然而,从用户的角度来看,这可能会使人很困惑。startTransition
API 来让 React 实现这一点:function handleTabSelect(tab) {
startTransition(() => {
setTab(tab);
});
}
'comments'
不会标记为紧急更新,而是标记为需要一些准备时间的 transition。**然后 React 会保留旧的 UI 并进行交互,当它准备好时,会切换为
,具体请参阅 Transitions 以了解更多相关信息。异常捕获边界(Error boundaries)
基于路由的代码分割
React.lazy
和 React Router 这类的第三方库,来配置基于路由的代码分割。import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</Router>
);
命名导出(Named Exports)
React.lazy
目前只支持默认导出(default exports)。如果你想被引入的模块使用命名导出(named exports),你可以**创建一个中间模块,来重新导出为默认模块。**这能保证 tree shaking 不会出错,并且不必引入不需要的组件。Context
应用场景
基本语法
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context(“light”为默认值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 “dark” 作为当前的值传递下去。
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() { return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。 static contextType = ThemeContext;
render() {
return <Button theme={this.context} />; }
}
优点
缺点
Link
和 Avatar
组件是如何使用 user
和 avatarSize
的。API
React.createContext
const MyContext = React.createContext(defaultValue);
Provider
中读取到当前的 context 值。defaultValue
参数才会生效。此默认值有助于在不使用 Provider 包装组件的情况下对组件进行测试。注意:将 undefined
传递给 Provider 的 value 时,消费组件的 defaultValue
不会生效。Context.Provider
value
属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。value
值发生变化时,它内部的所有消费组件都会重新渲染。从 Provider 到其内部 consumer 组件(包括 .contextType 和 useContext)的传播不受制于 shouldComponentUpdate
函数,因此当 consumer 组件在其祖先组件跳过更新的情况下也能更新。Object.is
相同的算法。Class.contextType
contextType
属性可以赋值为由 React.createContext()
创建的 Context 对象。此属性可以让你使用 this.context
来获取最近 Context 上的值。你可以在任何生命周期中访问到它,包括 render 函数中。MyClass.contextType = MyContext;
Context.Consumer
value
值等价于组件树上方离这个 context 最近的 Provider 提供的 value
值。如果没有对应的 Provider,value
参数等同于传递给 createContext()
的 defaultValue
。Context.displayName
displayName
的 property,类型为字符串。React DevTools 使用该字符串来确定 context 要显示的内容。const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';
<MyContext.Provider> // "MyDisplayName.Provider" 在 DevTools 中
<MyContext.Consumer> // "MyDisplayName.Consumer" 在 DevTools 中
在嵌套组件中更新 Context
// 确保传递给 createContext 的默认值数据结构是调用的组件(consumers)所能匹配的!
export const ThemeContext = React.createContext({
theme: themes.dark, toggleTheme: () => {},});
消费多个 Context
注意事项
value
属性值的浅比较),所以这里可能存在一些陷阱,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例子,当每一次 Provider 重渲染时,由于 value
属性总是被赋值为新的对象,以下的代码会重新渲染下面所有的 consumers 组件:class App extends React.Component {
render() {
return (
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'}, };
}
render() {
return (
错误边界
注意
setTimeout
或 requestAnimationFrame
回调函数)static getDerivedStateFromError()
或 componentDidCatch()
这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。当抛出错误后,请使用 static getDerivedStateFromError()
渲染备用 UI ,使用 componentDidCatch()
打印错误信息。catch {}
,不同的地方在于错误边界只针对 React 组件。只有 class 组件才可以成为错误边界组件。大多数情况下, 你只需要声明一次错误边界组件, 并在整个应用中使用它。catch {}
的工作机制。try
/ catch
语句:Refs 转发
ref
,并将其向下传递(换句话说,“转发”它)给子组件。FancyButton
使用 React.forwardRef
来获取传递给它的 ref
,然后转发到它渲染的 DOM button
:const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
FancyButton
的组件可以获取底层 DOM 节点 button
的 ref ,并在必要时访问,就像其直接使用 DOM button
一样。ref
只在使用 React.forwardRef
定义组件时存在。常规函数和 class 组件不接收 ref
参数,且 props 中也不存在 ref
。React.forwardRef
存在时有条件地使用它也是不推荐的:它改变了你的库的行为,并在升级 React 自身时破坏用户的应用。在高阶组件中转发 refs
function logProps(WrappedComponent) { class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
return
props
到其包裹的组件,所以渲染结果将是相同的。例如:我们可以使用该 HOC 记录所有传递到 “fancy button” 组件的 props:class FancyButton extends React.Component {
focus() {
// ...
}
// ...
}
// 我们导出 LogProps,而不是 FancyButton。
// 虽然它也会渲染一个 FancyButton。
export default logProps(FancyButton);
ref
不是 prop 属性。就像 key
一样,其被 React 进行了特殊处理。如果你对 HOC 添加 ref,该 ref 将引用最外层的容器组件,而不是被包裹的组件。FancyButton
组件的 refs 实际上将被挂载到 LogProps
组件:import FancyButton from './FancyButton';
const ref = React.createRef();
// 我们导入的 FancyButton 组件是高阶组件(HOC)LogProps。
// 尽管渲染结果将是一样的,
// 但我们的 ref 将指向 LogProps 而不是内部的 FancyButton 组件!
// 这意味着我们不能调用例如 ref.current.focus() 这样的方法
React.forwardRef
API 明确地将 refs 转发到内部的 FancyButton
组件。React.forwardRef
接受一个渲染函数,其接收 props
和 ref
参数并返回一个 React 节点。**例如:function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
const {forwardedRef, ...rest} = this.props;
// 将自定义的 prop 属性 “forwardedRef” 定义为 ref
return <Component ref={forwardedRef} {...rest} />; }
}
// 注意 React.forwardRef 回调的第二个参数 “ref”。
// 我们可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
// 然后它就可以被挂载到被 LogProps 包裹的子组件上。
return React.forwardRef((props, ref) => { return <LogProps {...props} forwardedRef={ref} />; });}
Fragments
render() {
return (
<> >
,但它并不支持 key 或属性。key
是唯一可以传递给 Fragment
的属性。未来我们可能会添加对其他属性的支持,例如事件。高阶组件
const EnhancedComponent = higherOrderComponent(WrappedComponent);
使用 HOC 解决横切关注点问题
data
prop。HOC 不需要关心数据的使用方式或原因,而被包装组件也不需要关心数据是怎么来的。withSubscription
和包装组件之间的契约完全基于之间传递的 props。这种依赖方式使得替换 HOC 变得容易,只要它们为包装的组件提供相同的 prop 即可。不要改变原始组件,而应该使用组合。
componentDidUpdate
的 HOC 增强它,那么前面的 HOC 就会失效!同时,这个 HOC 也无法应用于没有生命周期的函数组件。function logProps(WrappedComponent) {
return class extends React.Component {
componentDidUpdate(prevProps) {
console.log('Current props: ', this.props);
console.log('Previous props: ', prevProps);
}
render() {
// 将 input 组件包装在容器中,而不对其进行修改。Good!
return
约定:将不相关的 props 传递给被包裹的组件
render() {
// 过滤掉非此 HOC 额外的 props,且不要进行透传
const { extraProp, ...passThroughProps } = this.props;
// 将 props 注入到被包装的组件中。
// 通常为 state 的值或者实例方法。
const injectedProp = someStateOrInstanceMethod;
// 将 props 传递给被包装组件
return (
约定:最大化可组合性
const NavbarWithRouter = withRouter(Navbar);
const CommentWithRelay = Relay.createContainer(Comment, config);
// React Redux 的 `connect` 函数
const ConnectedComment = connect(commentSelector, commentActions)(CommentList);
约定:包装显示名称以便轻松调试
withSubscription
,并且被包装组件的显示名称为 CommentList
,显示名称应该为 WithSubscription(CommentList)
:注意事项
不要在 render 方法中使用 HOC
render() {
// 每次调用 render 函数都会创建一个新的 EnhancedComponent
// EnhancedComponent1 !== EnhancedComponent2
const EnhancedComponent = enhance(MyComponent);
// 这将导致子树每次渲染都会进行卸载,和重新挂载的操作!
return
务必复制静态方法
import hoistNonReactStatic from 'hoist-non-react-statics';
function enhance(WrappedComponent) {
class Enhance extends React.Component {/*...*/}
hoistNonReactStatic(Enhance, WrappedComponent);
return Enhance;
}
Refs 不会被传递
ref
实际上并不是一个 prop - 就像 key
一样,它是由 React 专门处理的。如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件。React.forwardRef
API性能优化
各个命令
build/
目录中生成对应的生产版本。注意只有在生产部署前才需要执行这个命令。npm run build
npm start
虚拟化长列表
虚拟DOM
shouldComponentUpdate
来进行提速。该方法会在重新渲染前被触发。其默认实现总是返回 true
,让 React 执行更新:shouldComponentUpdate(nextProps, nextState) {
return true;
}
shouldComponentUpdate
中返回 false
来跳过整个渲染过程。其包括该组件的 render
调用以及之后的操作。React.PureComponent
以代替手写 shouldComponentUpdate()
。它用当前与之前 props 和 state 的浅比较覆写了 shouldComponentUpdate()
的实现。shouldComponentUpdate 的作用
shouldComponentUpdate
仅检查了 props.color
或 state.count
是否改变。如果这些值没有改变,那么这个组件不会更新。如果你的组件更复杂一些,你可以使用类似“浅比较”的模式来检查 props
和 state
中所有的字段,以此来决定是否组件需要更新。React 已经提供了一位好帮手来帮你实现这种常见的模式 - 你只要继承 React.PureComponent
就行了。所以这段代码可以改成以下这种更简洁的形式:React.PureComponent
来代替手写 shouldComponentUpdate
。但它只进行浅比较,所以当 props 或者 state 某种程度是可变的话,浅比较会有遗漏,那你就不能使用它了。当数据结构很复杂时,情况会变得麻烦。不可变数据的力量
Portals
ReactDOM.createPortal(child, container)
child
)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container
)是一个 DOM 元素。用法
render() {
// React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。
// `domNode` 是一个可以在任何位置的有效 DOM 节点。
return ReactDOM.createPortal(
this.props.children,
domNode
);
}
overflow: hidden
或 z-index
样式时,但你需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框:通过 Portal 进行事件冒泡
组件,无论其是否采用 portal 实现,父组件都能够捕获其事件。Profiler API
Profiler
测量一个 React 应用多久渲染一次以及渲染一次的“代价”。 它的目的是识别出应用中渲染较慢的部分,或是可以使用类似 memoization 优化的部分,**并从相关优化中获益。Profiler
能添加在 React 树中的任何地方来测量树中这部分渲染所带来的开销。 它需要两个 prop :一个是 id
(string),一个是当组件树中的组件“提交”更新的时候被React调用的回调函数 onRender
(function)。onRender
回调Profiler
需要一个 onRender
函数作为参数。 React 会在 profile 包含的组件树中任何组件 “提交” 一个更新的时候调用这个函数。 它的参数描述了渲染了什么和花费了多久。不使用 ES6
自动绑定
this
到这个组件实例。 你需要在 constructor 中显式地调用 .bind(this)
:方法
onClick={(e) => this.handleClick(e)}
。createReactClass
。Mixins
setInterval()
可以很容易实现这个功能,但需要注意的是,当你不再需要它时,你应该清除它以节省内存。React 提供了生命周期方法,这样你就可以知道一个组件何时被创建或被销毁了。让我们创建一个简单的 mixin,它使用这些方法提供一个简单的 setInterval()
函数,它会在组件被销毁时被自动清理。diff算法
设计动机
render()
方法,会创建一棵由 React 元素组成的树。在下一次 state 或 props 更新时,相同的 render()
方法会返回一棵不同的树。React 需要基于这两棵树之间的差别来判断如何高效的更新 UI,以保证当前 UI 与最新的树保持同步。
key
属性标识哪些子元素在不同的渲染中可能是不变的。Diffing 算法
对比不同类型的元素
componentWillUnmount()
方法。所有与之前的树相关联的 state 也会被销毁对比同一类型的元素
对比同类型的组件元素
UNSAFE_componentWillReceiveProps()
、UNSAFE_componentWillUpdate()
以及 componentDidUpdate()
方法。render()
方法,diff 算法将在之前的结果以及新的结果中进行递归。对子节点进行递归
Keys
Math.random()
生成的)会导致许多组件实例和 DOM 节点被不必要地重新创建,这可能导致性能下降和子组件中的状态丢失。Refs and the DOM
何时使用 Refs
勿过度使用 Refs
创建 Refs
React.createRef()
创建的,并通过 ref
属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef(); }
render() {
return ; }
}
访问 Refs
render
中的元素时,对该节点的引用可以在 ref 的 current
属性中被访问。const node = this.myRef.current;
ref
属性用于 HTML 元素时,构造函数中使用 React.createRef()
创建的 ref
接收底层 DOM 元素作为其 current
属性。ref
属性用于自定义 class 组件时,ref
对象接收组件的挂载实例作为其 current
属性。ref
属性,因为他们没有实例。ref
属性,因为它们没有实例:function MyFunctionComponent() { return ;
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef(); }
render() {
// This will *not* work!
return (
ref
,你可以使用 forwardRef
(可与 useImperativeHandle
结合使用),或者可以将该组件转化为 class 组件。ref
属性,只要它指向一个 DOM 元素或 class 组件:function CustomTextInput(props) {
// 这里必须声明 textInput,这样 ref 才可以引用它 const textInput = useRef(null);
function handleClick() {
textInput.current.focus(); }
return (
将 DOM Refs 暴露给父组件
回调 Refs
createRef()
创建的 ref
属性,你会传递一个函数。这个函数中接受 React 组件实例或 HTML DOM 元素作为参数,以使它们能在其他地方被存储和访问。Render Props
使用 Render Props 来解决横切关注点(Cross-Cutting Concerns)
写死在
组件中,并且有效地更改渲染的结果,我们可以为
提供一个函数 prop 来动态的确定要渲染什么 —— 一个 render prop。render
方法 让
能够动态决定什么需要渲染,而不是克隆
组件然后硬编码来解决特定的用例。render
prop 的
组件就能够告诉它当前鼠标坐标 (x, y) 要渲染什么。withMouse
HOC而不是
组件,你可以使用带有 render prop 的常规
轻松创建一个:// 如果你出于某种原因真的想要 HOC,那么你可以轻松实现
// 使用具有 render prop 的普通组件创建一个!
function withMouse(Component) {
return class extends React.Component {
render() {
return (
使用 Props 而非
render
render
的 prop 来使用这种模式。事实上, 任何被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 “render prop”。注意事项
将 Render Props 与 React.PureComponent 一起使用时要小心
React.PureComponent
带来的优势。因为浅比较 props 的时候总会得到 false,并且在这种情况下每一个 render
对于 render prop 将会生成一个新的值。严格模式
StrictMode
是一个用来突出显示应用程序中潜在问题的工具。与 Fragment
一样,StrictMode
不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告。StrictMode
目前有助于:
类型检查的方法
使用 PropTypes 进行类型检查
propTypes
仅在开发模式下进行检查。PropTypes
import PropTypes from 'prop-types';
MyComponent.propTypes = {
// 你可以将属性声明为 JS 原生类型,默认情况下
// 这些属性都是可选的。
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
// 任何可被渲染的元素(包括数字、字符串、元素或数组)
// (或 Fragment) 也包含这些类型。
optionalNode: PropTypes.node,
// 一个 React 元素。
optionalElement: PropTypes.element,
// 一个 React 元素类型(即,MyComponent)。
optionalElementType: PropTypes.elementType,
// 你也可以声明 prop 为类的实例,这里使用
// JS 的 instanceof 操作符。
optionalMessage: PropTypes.instanceOf(Message),
// 你可以让你的 prop 只能是特定的值,指定它为
// 枚举类型。
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 一个对象可以是几种类型中的任意一个类型
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 可以指定一个数组由某一类型的元素组成
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 可以指定一个对象由某一类型的值组成
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
// 可以指定一个对象由特定的类型值组成
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// An object with warnings on extra properties
optionalObjectWithStrictShape: PropTypes.exact({
name: PropTypes.string,
quantity: PropTypes.number
}),
// 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保
// 这个 prop 没有被提供时,会打印警告信息。
requiredFunc: PropTypes.func.isRequired,
// 任意类型的必需数据
requiredAny: PropTypes.any.isRequired,
// 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。
// 请不要使用 `console.warn` 或抛出异常,因为这在 `oneOfType` 中不会起作用。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},
// 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。
// 它应该在验证失败时返回一个 Error 对象。
// 验证器将验证数组或对象中的每个值。验证器的前两个参数
// 第一个是数组或对象本身
// 第二个是他们当前的键。
customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matchme/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
})
};
PS