Context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}

function Toolbar(props) {
// Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。
// 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
// 因为必须将这个值层层传递所有组件。
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}

class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}

使用 Context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 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} />
}
}

注意:如果只是想避免层层传递属性,组件组合可能是更好的解决方案

API

[React.createContext]

[Context.Provider]

一个 Provide 可以和多个消费组件有对应关系,多个 Provide 可以嵌套使用,里层数据覆盖外层
当 Provide 的 value 值发生改变时,内部所有的消费组件都会重新渲染

Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新

[Class.contextType]

1
2
3
4
5
6
7
class MyClass extends React.Component {
static contextType = MyContext // public class fields 语法
render() {
let value = this.context
/* 基于这个值进行渲染工作 */
}
}

挂载在 class 上的 contextType 属性会被重赋值为 Context 对象,类组件可以通过 this.context 消费最近的 context 值,可以在任何生命周期中访问,包括 render

[Context.Consumer]

React 组件也可以订阅到 context 变更,在函数式组件中完成订阅 context

[Context.displayName]

context 对象接受一个名为 displayName 的 property,类型为字符串

注意事项:

context 会使用参考标识(reference identity)来决定何时进行渲染,Provide 的 value 值最好是基本数据类型或内存地址

错误边界

UI 的 javascript 错误不应该导致整个应用崩溃。错误边界是一种 React 组件,可以捕获并打印发生在子组件树任何位置的 javascript 错误,并且提供备用UI渲染

无法捕获的错误:

  • 事件处理
  • 异步代码
  • 服务端渲染
  • 自身错误

使用方法:

class 组件中定义了 static getDerivedStateFromError()componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。前者 渲染备用 UI,后者打印错误信息

只有 class 组件才可以成为错误边界组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ErrorBoundary extends React.Component {
constructor(props) {
super(props)
this.state = { hasError: false }
}

static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true }
}

componentDidCatch(error, errorInfo) {
// 你同样可以将错误日志上报给服务器
logErrorToMyService(error, errorInfo)
}

render() {
if (this.state.hasError) {
// 你可以自定义降级后的 UI 并渲染
return <h1>Something went wrong.</h1>
}

return this.props.children
}
}

Refs 转发

React.forwardRef 来获取传递给它的 ref

1
2
3
4
5
6
7
8
9
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>