前端跳槽突围课:React18底层源码深入剖析
//xia仔のke:
3w ukoou com
useCallback、useMemo 分析 & 差别
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
根据官网文档的介绍我们可理解:在a和b的变量值不变的情况下,memoizedCallback的引用不变。即:useCallback的第一个入参函数会被缓存,从而达到渲染性能优化的目的。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
根据官方文档的介绍我们可理解:在a和b的变量值不变的情况下,memoizedValue的值不变。即:useMemo函数的第一个入参函数不会被执行,从而达到节省计算量的目的。
分析
// 在Hooks中获取上一次指定的props
const usePrevProps = value => {
const ref = React.useRef();
React.useEffect(() => {
ref.current = value;
});
return ref.current;
}
function App() {
const [count, setCount] = React.useState(0);
const [total, setTotal] = React.useState(0);
const handleCount = () => setCount(count + 1);
const handleTotal = () => setTotal(total + 1);
const prevHandleCount = usePrevProps(handleCount);
console.log('两次处理函数是否相等:', prevHandleCount === handleCount);
return (
<div>
<div>Count is {count}</div>
<div>Total is {total}</div>
<br/>
<div>
<button onClick={handleCount}>Increment Count</button>
<button onClick={handleTotal}>Increment Total</button>
</div>
</div>
)
}
ReactDOM.render(<App />, document.body)
我们重点看这一行
const handleCount = () => setCount(count + 1);
根据我们之前的理解,我们知道每次App组件渲染时这个handleCount都是重新创建的一个新函数。
const prevHandleCount = usePrevProps(handleCount);
console.log('两次处理函数是否相等:', prevHandleCount === handleCount);
我们也可以通过比较上一次的prevHandleCount和本次的handleCount。可以明确的知道每次渲染时handleCount都是重新创建的一个新函数。
问题:它有什么问题呢?当我们将handleCount作为props传递给其他组件时会导致像PureComponent、shouldComponentUpdate、React.memo等相关优化失效(因为每次都是不同的函数)
端跳槽突围课:React18底层源码深入剖析 - 实现Consumer
概念
首先要理解上下文(context)的作用以及提供者和消费者分别是什么,同时要思考这种模式解决的是什么问题(跨层级组件通信)。
context做的事情就是创建一个上下文对象,并且对外暴露提供者(通常在组件树中上层的位置)和消费者,在上下文之内的所有子组件, 都可以访问这个上下文环境之内的数据,并且不用通过props。可以理解为有一个集中管理state的对象,并限定了这个对象可访问的范围, 在范围之内的子组件都能获取到它内部的值。
提供者为消费者提供context之内的数据,消费者获取提供者为它提供的数据,自然就解决了上边的问题。
用法
这里要用到一个小例子,功能就是主题颜色的切换。效果如图:
根据上边的概念和功能,分解一下要实现的步骤:
- 创建一个上下文,来提供给我们提供者和消费者
- 提供者提供数据
- 消费者获取数据
这里的文件组织是这样的:
复制代码
├─context.js // 存放context的文件
│─index.js // 根组件,Provider所在的层级
│─Page.js // 为了体现跨层级通信的添加的一个中间层级组件,子组件为Title和Paragraph
│─Title.js // 消费者所在的层级
│─Paragraph.js // 消费者所在的层级
创建一个上下文
import React from 'react'
const ThemeContext = React.createContext()
export const ThemeProvider = ThemeContext.Provider
export const ThemeConsumer = ThemeContext.Consumer
这里,ThemeContext就是一个被创建出来的上下文,它内部包含了两个属性,看名字就可以知道,一个是提供者一个是消费者。 Provider和Consumer是成对出现的,每一个Provider都会对应一个Consumer。而每一对都是由React.createContext()创建出来的。
page组件
没啥好说的,就是一个容器组件而已
const Page = () => <>
<Title/>
<Paragraph/>
</>
提供者提供数据
提供者一般位于比较上边的层级,ThemeProvider 接受的value就是它要提供的上下文对象。
// index.js
import { ThemeProvider } from './context'
render() {
const { theme } = this.state
return <ThemeProvider value={{ themeColor: theme }}>
<Page/>
</ThemeProvider>
}