Open xiaochengzi6 opened 2 years ago
项目优化:
1、父组件嵌套子组件 当父组件更新时便会重新渲染整个组件这个过程是连带着子组件进行的
解决方法:[ React.mome ] 使用 React.memo
使其在组件准备更新时进行 props
的对比工作(浅层比较)
2、父组件嵌套子组件,父组件往子组件传值 但子组件没有使用 当父组件更新时就会带着子组件跟新
解决方法:[ React.memo + useMemo ] 使用 useMemo
用来包裹父组件往子组件传入的属性 并且子组件使用 React.memo 包裹
当然可能有更复杂的情况需要使用 React.memo() 的第二参数来进行深度对比从而减少子组件的渲染
合理的渲染方式以及代码结构才能带来整体的性能优化 随意使用 hook 更是一种灾难
考虑到闭包的影响
useEffect(() => {
// 回调函数只运行一次,这里的 count 只记住初次渲染的那个值
// 所以导致每一次的 setInterval 中用到的永远都不会变!
const id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, []);
由于 useEffect的依赖参数为空数组所以创建了一个匿名函数却不会修改它,这个时候就出现了闭包陷进,该函数始终记录整个 count
变量的值为 0 所以 count 只会到 1 就不会再发生变化
或者参考 [Dan] (https://overreacted.io/zh-hans/making-setinterval-declarative-with-react-hooks/)的示例
const callbackRef = useRef(null)
const callback = () => setCount(count + 1)
useEffect(() => {
callbackRef.current = callback
})
useEffect(() => {
function tick() {
savedCallback.current();
}
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []);
React.memo(componet, arePropsEqual) 会返回一个新组件,该组件和原来组件相同, 被 react.memo 包裹并不会再父组件跟新的时候更新,它通过第二个参数进行对比,通常并不需要提供该函数,一般默认就有,使用 Object.is
相同的算法
约束条件:
1、只能在函数内部的最外层调用 Hook,不要在循环、条件判断或者子函数中调用 2、只能在 React 的函数组件中调用 Hook,不要在其他 JavaScript 函数中调用
一、useState
useState出现,使得react无状态组件能够像有状态组件一样,可以拥有自己state,useState的参数可以是一个具体的值,也可以是一个函数用于判断复杂的逻辑,函数返回作为初始值,usestate 返回一个数组,数组第一项用于读取此时的state值 ,第二项为派发数据更新,组件渲染的函数,函数的参数即是需要更新的值。
useState派发更新函数的执行,就会让整个function组件从头到尾执行一次
class 组件通过一个实例化对象后往后的每一次更新只用调用 render 函数就行。而函数组件是每一次都会重新执行。
二、useEffect
如果不给useEffect执行加入限定条件,函数组件每一次更新都会触发effect ,那么也就说明每一次state更新,或是props的更新都会触发useEffect执行,
合理的用于useEffect就要给effect加入限定执行的条件,也就是useEffect的第二个参数,这里说是限定条件,也可以说是上一次useeffect更新收集的某些记录数据变化的记忆,在新的一轮更新,useeffect会拿出之前的记忆值和当前值做对比,如果发生了变化就执行新的一轮useEffect的副作用函数,useEffect第二个参数是一个数组,用来收集多个限制条件 。
执行时机:给
useEffect
的函数会在浏览器完成布局与绘制之后,在一个延迟事件中被调用。useLayoutEffect
功能相似但是它和useEffect 的不同之处在于执行顺序
前者可能会造成页面的
闪动
后者可能会造成页面的卡顿
三、(一)useRef
useRef 每次渲染返回同一个 Ref 的对象 并且它发生变化时 useRef 也不会做出什么反应 解决方法:
回调Ref
在这里我了解到的用处有 1. 访问DOM元素 2.缓存数据。
先来了解一下 ref 在类组件中可以通过创建 ref 和在 dom 节点获取的方式
在默认的情况下函数组件内部不能使用 ref 属性 因为他们没有任何实例。你可以采用 forwardRef 的来找到 ref
1.当然在函数组件中使用 useRef hook 来获取函数组件中的 dom 节点或者 class 组件。
当然useRef还有一个很重要的作用就是缓存数据,我们知道usestate ,useReducer 是可以保存当前的数据源的,但是如果它们更新数据源的函数执行必定会带来整个组件从新执行到渲染,如果在函数组件内部声明变量,则下一次更新也会重置,如果我们想要悄悄的保存数据,而又不想触发函数的更新,那么useRef是一个很棒的选择。
(二)、useImperativeHandle
useImperativeHandle
可以让你在使用ref
时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle
应当与 [forwardRef
] 一起使用:四、useCallback
在第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行。
这里看以清楚的看出来 useCallback 返回的函数 且这个函数依赖与 textRef 这个ref 它绑定的使 input 中的文本节点
text的变化就是 current 的变化 无论current 的值怎么变化 ref 没有任何变动 ( {current: value} 这个对象还是那个对象)所以子组件不会更新。
五、useMemo
把“创建”函数和依赖项数组作为参数传入
useMemo
,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。1.案例一:如果组件中父组件更新那么会势必带动其内的子组件的更新。(组件件没有传入任何值)
使用React.memo()后 如果
props 没有发生改变
,则不会重新渲染
此组件。2.案例二:如果父组件往子组件传入值 且子组件使用 React.memo() 包裹子组件并没有去使用父组件传入的值 当父组件的更新也会带动子组件的更新
useMemo 缓存的是属性。当缓存依赖没改变,去获取曾经的缓存。 将 remo 和 useMemo 结合使用来去避免案例二的问题出现。
注意
:useMemo 和 useCallback 很相似都能配合 React.memo() 来做到优化 前者缓存属性后者缓存函数。但前者能做到更多。简单来说: useCallback 就是处理父组件重复无用的调用子组件的。 useMemo 就是处理重复无用的调用属性的。
这个时候就算Image组件不使用 userInfrom 父组件一旦更新子组件也会更新。 因为父组件点击后调用了一次 setCount 就会使状态更新从而父组件重新运行一次后导致 子组件的 userInfrom 的重新创建 就算子组件被 React.memo() 包裹也不行
解决办法就是使用 useMemo hook 来缓存属性。
此时子组件就不会随便更新了 但要注意 useMemo 要和React.memo 搭配使用、
六、useContext
我们可以使用useContext ,来获取父级组件传递过来的context值,这个当前值就是最近的父级组件 Provider 设置的value值,useContext参数一般是由 createContext 方式引入 ,也可以父级上下文context传递 ( 参数为context )。useContext 可以代替 context.Consumer 来获取Provider中保存的value值
七、useReducer
它的第一个参数可以看作是一个 reducer 其中接收 state,action 作为参数。第二个参数为初始值。 useReducer 返回一个数组。数组第一个值为数据 初始值在第二个参数给出。第二个值为dispatch函数用来传递action 从而来改变数据。
八、自定义hook
自定义 hook 名字必须以 use 开头函数内部调用了其他的 hook 它并不服用 state 本身 它的存在是复用其状态的逻辑。每次调用 hook 都是一次全新的 state 。不会共享状态。