ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] React 16版本有一个名为 Fiber 的新核心架构。 # 事件机制 # setState # `React.lazy` 与 `React.Suspense` > `React.lazy`和 Suspense 技术还不支持服务端渲染。如果你想要在使用服务端渲染的应用中使用,我们推荐[Loadable Components](https://github.com/gregberge/loadable-components)这个库。它有一个很棒的[服务端渲染打包指南](https://loadable-components.com/docs/server-side-rendering/)。 > `React.lazy`目前只支持默认导出(default exports) `React.Suspense`也是一种虚拟组件(类似于[Fragment](https://reactjs.org/docs/react-api.html#reactfragment),仅用作类型标识),用法如下: ``` import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom'; import React, { Suspense, lazy } from 'react'; const Home = lazy(() => import('./Home')); const Bar = lazy(() => import('./Bar')); const App = () => ( <Router> <Suspense fallback={<div>loading</div>}> <div> <ul> <li><Link to="/">Home></Link></li> <li><Link to="/bar">Bar></Link></li> </ul> </div> <Switch> <Route exact path="/" component={Home} /> <Route path="/bar" component={Bar} /> </Switch> </Suspense> </Router> ) export default App; ``` `Suspense`子树中只要存在还没回来的 Lazy 组件,就走`fallback`指定的内容。**这不正是可以提升到任意祖先级的loading吗?** http://www.ayqy.net/blog/react-suspense/ # React Context React的 context 就是一个全局变量,可以从根组件跨级别在 React 的组件中传递。React context 的API有两个版本,React16.x 之前的是 老版本的 context,之后的是新版本的 context。 新版本的 React context 使用了`Provider`和`Customer`模式,和 react-redux 的模式非常像。在顶层的`Provider`中传入`value`, 在子孙级的`Consumer`中获取该值,并且能够传递函数,用来修改 context,如下代码所示: ``` import React, { createContext } from 'react'; // 创建Context的唯一方法 const ThemeContext = createContext() const SizeContext = createContext() class App extends React.Component { state = { theme: 'red', size: 'small' } render () { const { theme, size } = this.state return ( // 使用 Context.Provider 包裹后续组件,value 指定值 <ThemeContext.Provider value={theme}> {/* 当出现多个Context的时候,只需要将Context.Provider 嵌套即可 */} <SizeContext.Provider value={size}> {/* 当Context的Provider值更改时,Consumer 的值必须重新渲染 */} <button onClick={() => {this.setState({ theme: 'yellow', size: 'big'})}}>按钮</button> <Middle></Middle> </SizeContext.Provider> </ThemeContext.Provider> ) } } class Bottom extends React.Component { render () { return ( // Context.Consumer Consumer 消费者使用 Context 得值 // 但子组件不能是其他组件,必须渲染一个函数,函数的参数就是 Context 得值 // 当出现 多个 Consumer 的时候,进行嵌套,每个 Consumer 的子组件必须是一个函数,即可 <ThemeContext.Consumer> { theme => ( <SizeContext.Consumer> { size => (<h1>ThemeContext 的 值为 {theme}; SizeContext 的值为 {size}</h1>) } </SizeContext.Consumer> ) } </ThemeContext.Consumer> ) } } class Middle extends React.Component { render () { return <Bottom></Bottom> } } export default App; ``` * 当 Provider 提供的值更改时,Consumer 必须重新渲染 * context 不仅仅只是可以传数值,也可以传函数。 * 创建 Context 的时候`createContext`可以传入默认值,当向上找不到 Provider 的时候,就会显示默认值 > 注意:context 类似于全局变量做法,会让组件失去独立性、复用起来更困难,不能滥用、但本身它一定有适合使用的场景,具体看情况使用 ## contextType React 16.6 引入了在不直接使用 Consumer 组件的情况下从上下文消费数据的功能。这有助于减少组件 JSX 中不必要的嵌套,使它们更易于阅读。 ``` import React, { createContext } from 'react'; // 创建Context的唯一方法 const ThemeContext = createContext() const SizeContext = createContext() class App extends React.Component { state = { theme: 'red', size: 'small' } render () { const { theme, size } = this.state return ( // 使用 Context.Provider 包裹后续组件,value 指定值 <ThemeContext.Provider value={theme}> {/* 当出现多个Context的时候,只需要将Context.Provider 嵌套即可 */} <SizeContext.Provider value={size}> {/* 当Context的Provider值更改时,Consumer 的值必须重新渲染 */} <button onClick={() => {this.setState({ theme: 'yellow', size: 'big'})}}>按钮</button> <Middle></Middle> </SizeContext.Provider> </ThemeContext.Provider> ) } } class Bottom extends React.Component { // 申明静态变量、contextType 将 context 直接赋值于 contextType static contextType = ThemeContext render () { // 在 render 函数中 可以直接 访问 this.context 获取共享变量、这样就可以不使用 consumer const theme = this.context return ( // Context.Consumer Consumer消费者使用Context得值 // 但子组件不能是其他组件,必须渲染一个函数,函数的参数就是Context得值 // 当出现 多个Consumer的时候,进行嵌套,每个Consumer 的子组件必须是一个函数,即可 <div> <h1>ThemeContext 的 值为 {theme} </h1> </div> ) } } class Middle extends React.Component { render () { return <Bottom></Bottom> } } export default App; ``` * `contextType`只能在类组件中使用 * 一个组件如果有多个consumer, contextType 只对其中一个有效,所以说,`contextType`只能有一个 ## React context的局限性 1. 在组件树中,如果中间某一个组件 `ShouldComponentUpdate` `returning false` 了,会阻碍 context 的正常传值,导致子组件无法获取更新。 2. 组件本身 extends `React.PureComponent` 也会阻碍 context 的更新。 注意点: 1. Context 应该是唯一不可变的 2. 组件只在初始化的时候去获取 Context > [context.html#classcontexttype](https://reactjs.org/docs/context.html#classcontexttype) > [React context基本用法](https://www.cnblogs.com/mengff/p/9511419.html) # 新的生命周期 需要关注新的[声明周期](https://zh-hans.reactjs.org/docs/react-component.html): ![](https://img.kancloud.cn/a5/04/a504a443428b20eadc2778941ad1779c_904x617.png) 目前在 16.4 版本中: * ~~componentWillMount~~, * ~~componentWillReceiveProps~~, * ~~componentWillUpdate~~ 并未完全删除这三个生命周期函数,而且新增了 * `UNSAFE_componentWillMount`, * `UNSAFE_componentWillReceiveProps`, * `UNSAFE_componentWillUpdate` 三个函数,官方计划在 17 版本完全删除这三个函数,只保留 `UNSAVE_` 前缀的三个函数,目的是为了向下兼容,但是对于开发者而言应该尽量避免使用他们,而是使用新增的生命周期函数替代它们 **取而代之的生命周期** 1. [static getDerivedStateFromProps(nextProps, prevState)](https://zh-hans.reactjs.org/docs/react-component.html#static-getderivedstatefromprops) 2. [getSnapshotBeforeUpdate(prevProps, prevState)](https://zh-hans.reactjs.org/docs/react-component.html#getsnapshotbeforeupdate) ## `getDerivedStateFromProps` 当我们接收到新的`props`,想去修改我们state,可以使用`getDerivedStateFromProps`。 ``` class ExampleComponent extends React.Component { state = { isScrollingDown: false, lastRow: null } static getDerivedStateFromProps(nextProps,prevState){ if(nextProps.currentRow !== prevState.lastRow){ return { isScrollingDown: nextProps.currentRow &gt; prevState.lastRow, lastRow: nextProps.currentRow } } return null; } } ``` ## `getSnapshotBeforeUpdate` 这个函数有一个返回值,会作为第三个参数传给 `componentDidUpdate`,如果你不想要返回值,请返回 `null`,不写的话控制台会有警告 ``` class ScrollingList extends React.Component { constructor(props) { super(props); this.listRef = React.createRef(); } getSnapshotBeforeUpdate(prevProps, prevState) { //表示之前的props 和之前的 state // Are we adding new items to the list? // Capture the scroll position so we can adjust scroll later. if (prevProps.list.length < this.props.list.length) { const list = this.listRef.current; return list.scrollHeight - list.scrollTop; } return null; } componentDidUpdate(prevProps, prevState, snapshot) { // If we have a snapshot value, we've just added new items. // Adjust scroll so these new items don't push the old ones out of view. // (snapshot here is the value returned from getSnapshotBeforeUpdate) if (snapshot !== null) { const list = this.listRef.current; list.scrollTop = list.scrollHeight - snapshot; } } render() { return ( <div ref={this.listRef}>{/* ...contents... */}</div> ); } } ``` ## `componentDidUpdate(prevProps, prevState, snapshot)` 该方法在`getSnapshotBeforeUpdate`方法之后被调用,有三个参数`prevProps,prevState,snapshot`,**表示之前的props,之前的state,和snapshot**。第三个参数是`getSnapshotBeforeUpdate`返回的。 ## 参考 [生命周期图](http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/) [官网 - 组件的生命周期](https://zh-hans.reactjs.org/docs/react-component.html#the-component-lifecycle) [[译]如何使用React生命周期方法](https://juejin.im/post/5b59d1c8e51d4519455846e0) [React 16.6.X版本的更新功能](https://www.jianshu.com/p/406bcc058790) [盘点 React 16.0 ~ 16.5 主要更新及其应用](https://www.cnblogs.com/sunshq/p/10430728.html)