React的顶层API
React 是 React 库的入口。如果你通过使用
<script>
标签的方式来加载 React,则可以通过 React 全局变量对象来获得 React 的顶层 API。当你使用 ES6 与 npm 时,可以通过编写import React from 'react'
来引入它们。当你使用 ES5 与 npm 时,则可以通过编写var React = require('react')
来引入它们。
目前可以大致分为以下几类:
- 组件类: React.Component 、 React.PureComponent、React.memo;
- 创建React元素:React.createElement()、React.createFactory();
- 转换元素:React.cloneElement()、React.isValidElement()、React.Children;
- Fragments:React.Fragment 片段;
- 引用Refs:React.createRef、React.forwardRef;
- 动态加载:React.lazy、React.Suspense;
- Hook 是 React 16.8 的新增特性,后面会单独讲解。
组件类详析
- React.Component 是使用 ES6 classes 方式定义 React 组件的基类:
class Greeting extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
- React.PureComponent
React.PureComponent
与React.Component
很相似。两者的区别在于React.Component
并未实现shouldComponentUpdate()
,而React.PureComponent
中以浅层对比 prop 和 state 的方式来实现了该函数。React.PureComponent
中的shouldComponentUpdate()
仅作对象的浅层比较。如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层比较。此外,React.PureComponent 中的 shouldComponentUpdate() 将跳过所有子组件树的 prop 更新。因此,请确保所有子组件也都是“纯”的组件。 - React.memo
React.memo 为高阶组件。它与 React.PureComponent 非常相似,但它适用于函数组件,但不适用于 class 组件。 默认情况下其只会对复杂对象做浅层对比,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现。const MyComponent = React.memo(function MyComponent(props) { /* 使用 props 渲染 */ });
此方法仅作为性能优化的方式而存在。但请不要依赖它来“阻止”渲染,因为这会产生 bug。function MyComponent(props) { /* 使用 props 渲染 */ } function areEqual(prevProps, nextProps) { /* 如果把 nextProps 传入 render 方法的返回结果与 将 prevProps 传入 render 方法的返回结果一致则返回 true, 否则返回 false */ } export default React.memo(MyComponent, areEqual);
创建元素
- JSX的语法内在:createElement()
创建并返回指定类型的新 React 元素。其中的类型参数既可以是标签名字符串(如 'div' 或 'span'),也可以是 React 组件 类型 (class 组件或函数组件),或是 React fragment 类型。React.createElement( type, [props], [...children] )
- createFactory()
React.createFactory(type)
返回用于生成指定类型 React 元素的函数。与 React.createElement() 相似的是,类型参数既可以是标签名字符串(像是 'div' 或 'span'),也可以是 React 组件 类型 (class 组件或函数组件),或是 React fragment 类型。 此辅助函数已废弃,建议使用 JSX 或直接调用 React.createElement() 来替代它。
转变元素
- 克隆元素:cloneElement()
以 element 元素为样板克隆并返回新的 React 元素。返回元素的 props 是将新的 props 与原始元素的 props 浅层合并后的结果。新的子元素将取代现有的子元素,而来自原始元素的 key 和 ref 将被保留。 React.cloneElement() 几乎等同于:React.cloneElement( element, [props], [...children] )
<element.type {...element.props} {...props}>{children}</element.type>
但是,这也保留了组件的 ref。这意味着当通过 ref 获取子节点时,你将不会意外地从你祖先节点上窃取它。相同的 ref 将添加到克隆后的新元素中。 最为常见的场景就是:- 克隆某个组件的所有子元素,并添加新的props到各组件子元素中;
const pageCmp = React.Children.map( children, (child: any, index) => React.cloneElement( child, { index, activeIndex: this.state.activeIndex, } ) );
- 实现sticky的功能;
- 克隆某个组件的所有子元素,并添加新的props到各组件子元素中;
- isValidElement()
React.isValidElement(object)
,验证对象是否为 React 元素,返回值为 true 或 false。 - React.Children
React.Children 提供了用于处理 this.props.children 不透明数据结构的实用方法。
- React.Children.map 遍历
React.Children.map(children, function[(thisArg)])
在 children 里的每个直接子节点上调用一个函数,并将 this 设置为 thisArg。如果 children 是一个数组,它将被遍历并为数组中的每个子节点调用该函数。如果子节点为 null 或是 undefined,则此方法将返回 null 或是 undefined,而不会返回数组。如果 children 是一个 Fragment 对象,它将被视为单一子节点的情况处理,而不会被遍历。 - React.Children.forEach
React.Children.forEach(children, function[(thisArg)])
,与 React.Children.map() 类似,但它不会返回一个数组。 - React.Children.count
React.Children.count(children),
返回 children 中的组件总数量,等同于通过 map 或 forEach 调用回调函数的次数。 - React.Children.only
React.Children.only(children)
, 验证 children 是否只有一个子节点(一个 React 元素),如果有则返回它,否则此方法会抛出错误。 - React.Children.toArray
React.Children.toArray(children)
,将 children 这个复杂的数据结构以数组的方式扁平展开并返回,并为每个子节点分配一个 key。当你想要在渲染函数中操作子节点的集合时,它会非常实用,特别是当你想要在向下传递 this.props.children 之前对内容重新排序或获取子集时。
- React.Children.map 遍历
片段
React的16.3开始,React.Fragment
组件能够在不额外创建 DOM 元素的情况下,让 render() 方法中返回多个元素。
render() {
return (
<React.Fragment>
Some text.
<h2>A heading</h2>
</React.Fragment>
);
}
你也可以使用其简写语法 <></>,但是不支持添加属性。
ref相关
React.createRef React.createRef 创建一个能够通过 ref 属性附加到 React 元素的 ref。
class MyComponent extends React.Component { constructor(props) { super(props); this.inputRef = React.createRef(); } render() { return <input type="text" ref={this.inputRef} />; } componentDidMount() { this.inputRef.current.focus(); } }
React.forwardRef React.forwardRef 会创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。这种技术并不常见,但在以下两种场景中特别有用:
- 转发 refs 到 DOM 组件
在高阶组件中转发 refs React.forwardRef 接受渲染函数作为参数。React 将使用 props 和 ref 作为参数来调用此函数。此函数应返回 React 节点。
const FancyButton = React.forwardRef((props, ref) => ( <button ref={ref} className="FancyButton"> {props.children} </button> )); // You can now get a ref directly to the DOM button: const ref = React.createRef(); <FancyButton ref={ref}>Click me!</FancyButton>;
异步加载
- React.lazy
React.lazy() 允许你定义一个动态加载的组件。这有助于缩减 bundle 的体积,并延迟加载在初次渲染时未用到的组件。
const SomeComponent = React.lazy(() => import('./SomeComponent'));
, 请注意,渲染 lazy 组件依赖该组件渲染树上层的组件。这是指定加载指示器(loading indicator)的方式。 React.Suspense React.Suspense 可以指定加载指示器(loading indicator),以防其组件树中的某些子组件尚未具备渲染条件。目前,懒加载组件是
支持的唯一用例: // 该组件是动态加载的 const OtherComponent = React.lazy(() => import('./OtherComponent')); function MyComponent() { return ( // 显示 <Spinner> 组件直至 OtherComponent 加载完成 <React.Suspense fallback={<Spinner />}> <div> <OtherComponent /> </div> </React.Suspense> ); }