We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
本篇是偏向手册类型的总结性文章。
如果你还有其他关于触发 React 更新和优化 React 不必要的更新的 API 或者方法,请在下面评论告诉我,感谢!
当 props 或者 state 发生变化时,类组件根据 shouldComponentUpdate 的返回值来决定是否更新组件。shouldComponentUpdate 默认始终返回 true,因此当 props 或者 state 发生变化时,默认情况下类组件始终会更新。
props
state
shouldComponentUpdate
true
shouldComponentUpdate(nextProps, nextState);
注意,shouldComponentUpdate 返回 false 虽然会阻止组件更新和渲染,但是 this.setState 仍会更新 state 的值。
false
this.setState
setState
component.forceUpdate(callback);
调用 forceUpdate() 强制让组件重新渲染;致使组件调用 render() 方法;此操作会跳过该组件的 shouldComponentUpdate()。
forceUpdate()
render()
shouldComponentUpdate()
对于 Provider 组件的 value 更新时,文档中有这样一句话:
value
当 Provider 组件的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 组件及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。
这句话什么意思呢,举个例子:
import React, { Component } from 'react'; import { render } from 'react-dom'; const CountCtx = React.createContext(0); class Child extends Component { shouldComponentUpdate() { return false; } render() { return <CountCtx.Consumer>{(count) => <h1>{count}</h1>}</CountCtx.Consumer>; } } class App extends Component { state = { count: 0 }; inc = () => { this.setState({ count: this.state.count + 1 }); }; render() { return ( <CountCtx.Provider value={this.state.count}> <button onClick={this.inc}>inc</button> <Child /> </CountCtx.Provider> ); } } render(<App />, document.getElementById('root'));
例子中,即使 Child 的 shouldComponentUpdate 始终返回 false,CountCtx.Consumer 组件也会更新。
Child
CountCtx.Consumer
useContext
Redux 的 connect 也有类似的不受祖先组件限制能够直接更新深层次子消费组件的效果。
connect
父子组件在组合嵌套使用时,有以下 2 种使用场景,这 2 种场景下,组件更新的表现也会有所不同:
// 场景一,子组件直接写在父级组件内 function Parent() { const [parentCount, setParentCount] = useState(0); console.log('parent update'); return ( <div> <p>Parent component count: {parentCount}</p> <input defaultValue={parentCount} onBlur={(e) => { setParentCount(+e.target.value); }} /> <br /> <br /> <Child /> </div> ); } function Child(props) { const [childCount, setChildCount] = useState(0); console.log('child update'); return ( <div> <p>Child Component count:{childCount}</p> <button onClick={() => { setChildCount((preCount) => preCount + 1); }} > add child count </button> </div> ); }
// 场景二,子组件通过children的形式传入父级组件 function App() { return ( <Parent> <Child /> </Parent> ); } function Parent(props) { const [parentCount, setParentCount] = useState(0); console.log('parent update'); return ( <div> <span>Parent component count: {parentCount}</span> <input defaultValue={parentCount} onBlur={(e) => { setParentCount(+e.target.value); }} /> <br /> <br /> {props.children} </div> ); } function Child(props) { const [childCount, setChildCount] = useState(0); console.log('child update'); return ( <div> <span>Child Component count:{childCount}</span> <button onClick={() => { setChildCount((preCount) => preCount + 1); }} > add child count </button> </div> ); }
造成这种区别的主要原因是,在编译阶段编译 JSX 时:
Parent
React.createElement(Child)
App
props.children
关于这部分内容,你也可以阅读下面两篇文章获得更多理解: 1、 我在大厂写 React,学到了什么?性能优化篇 2、 React 组件到底什么时候 render 啊
this.props
nextProps
this.state
nextState
React.PureComponent
prop
shouldComponentupdate()
React.Component
React.memo
useState
context
function MyComponent(props) { /* 使用 props 渲染 */ } function areEqual(prevProps, nextProps) { /* 如果把 nextProps 传入 render 方法的返回结果与 将 prevProps 传入 render 方法的返回结果一致则返回 true, 否则返回 false */ } export default React.memo(MyComponent, areEqual);
const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]);
useCallback
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useMemo
The text was updated successfully, but these errors were encountered:
No branches or pull requests
本篇是偏向手册类型的总结性文章。
如果你还有其他关于触发 React 更新和优化 React 不必要的更新的 API 或者方法,请在下面评论告诉我,感谢!
第一部分:更新
基础更新
类组件
当
props
或者state
发生变化时,类组件根据shouldComponentUpdate
的返回值来决定是否更新组件。shouldComponentUpdate
默认始终返回true
,因此当props
或者state
发生变化时,默认情况下类组件始终会更新。注意,
shouldComponentUpdate
返回false
虽然会阻止组件更新和渲染,但是this.setState
仍会更新state
的值。函数式组件,Hooks
props
更新时,新旧props
不相等时组件会更新。setState
hook 更新state
,新旧state
不相等时组件会更新;新旧state
相等时,在第一次更新state
时,组件也会更新,之后更新相同的state
值,组件不会更新。forceUpdate
调用
forceUpdate()
强制让组件重新渲染;致使组件调用render()
方法;此操作会跳过该组件的shouldComponentUpdate()
。Provider 和 consumer 的更新
对于 Provider 组件的
value
更新时,文档中有这样一句话:这句话什么意思呢,举个例子:
例子中,即使
Child
的shouldComponentUpdate
始终返回false
,CountCtx.Consumer
组件也会更新。value
值发生变化时,Provider 组件和它内部的 consumer 组件都会更新,且 consumer 组件不受祖先组件的shouldComponentUpdate
影响,会直接更新。useContext
Hook 也是会在 Provider 组件更新时触发其所在的组件更新。子组件与 children
父子组件在组合嵌套使用时,有以下 2 种使用场景,这 2 种场景下,组件更新的表现也会有所不同:
造成这种区别的主要原因是,在编译阶段编译 JSX 时:
Parent
组件内写的子组件Child
,会使用React.createElement(Child)
进行编译,返回编译后的 React Node 对象,因此每次Parent
组件更新,Parent
的 JSX 都会重新编译,Child
组件也会被重新编译,既然Child
组件重新编译了,返回的自然就是另一个 React Node 对象,即便他们看上去一样,但是他们都是引用类型的数据,因此他们不相等,子组件会更新。Child
是先在App
组件内编译成 React Node 再通过props
传给Parent
组件的,因此Parent
组件更新时,通过props.children
拿到的始终是最开始编译Child
组件返回的 React Node 对象,它始终没有改变,因此子组件Child
不会更新。第二部分:优化
shouldComponentUpdate
shouldComponentUpdate
默认返回true
,即类组件在props
或state
发生变化时,不论新旧值是否相等,默认总是会更新。shouldComponentUpdate
,可以将this.props
与nextProps
以及this.state
与nextState
进行比较,通过返回true
或者false
来决定是否更新当前组件。React.PureComponent
React.PureComponent
中以浅层对比prop
和state
的方式来实现shouldComponentupdate()
。React.PureComponent
中的shouldComponentUpdate()
将跳过所有子组件树的prop
更新。因此,请确保所有子组件也都是“纯”的组件。React.PureComponent
可以自动处理纯组件的更新优化,避免在React.Component
中手写shouldComponentUpdate
。React.memo
React.memo
仅检查props
变更。如果你的函数组件在给定相同props
的情况下渲染相同的结果,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。React.memo
包裹,且其实现中拥有useState
或useContext
的 Hook,当context
或state
发生变化时,它仍会重新渲染。React.memo
只会对复杂对象做浅层对比,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现。useCallback
useCallback
返回一个 memoized 回调函数,该回调函数仅在某个依赖项改变时才会更新。shouldComponentUpdate
)的子组件时,它将非常有用。useMemo
useMemo
,它返回函数执行的结果,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。shouldComponentUpdate
)的子组件时,它将非常有用。The text was updated successfully, but these errors were encountered: