网站建设要会什么软件,织梦培训机构网站模板,建设内部网站目的,西地那非片的功能主治说明书简介
正常来讲的话当我们点击组件的时候#xff0c;该组件以及该组件的子组件都会重新渲染#xff0c;但是如何避免子组件重新渲染呢#xff0c;我们经常用memo来解决
React.memo配合useCallback缓存组件
父组件没有传props
const Index () {console.log(子组件刷新…简介
正常来讲的话当我们点击组件的时候该组件以及该组件的子组件都会重新渲染但是如何避免子组件重新渲染呢我们经常用memo来解决
React.memo配合useCallback缓存组件
父组件没有传props
const Index () {console.log(子组件刷新了);return (div这是子组件/div)
}
//这里我们用react.memo对组件进行包裹包裹一次之后react在render的过程中不会给该fiber打上更新的tag
//从而跳过更新这个原理其实就是react.memo的第二个参数上如果react.memo第二个参数不传递react回默
//认给我们补充上第二个参数的逻辑其中逻辑就是浅比较Index组件的props参数如果相等的话默认第二个参数返
//回true组件就会缓存了如果不相等的话就会返回false组件就会重新打上更新的tag然后重新渲染。
const MemoIndex React.memo(Index);const App (){const [state, setState] useState(0);return (div classNameAppbutton onClick{()setState(state1)}点我看看子组件刷新了吗/buttonMemoIndex//div);
}
父组件传了state
const Index () {console.log(子组件刷新了);return (div这是子组件/div)
}
//其实这Index组件不会更新的react检测到我们不传递第二个参数的话会把之前的props拆出来和现在的
//props做比较 发现pre.next cur.next 然后回返回true组件就会缓存了
const MemoIndex React.memo(Index);
//这行代码就相当这样的代码
const MemoIndex React.memo(Index, (pre, cur) {//这样写的就比较简单了因为这是是针对于当前的demo来说的。react比较的代码逻辑比较复杂因为react//需要考虑到多种情况props中参数可能多一个少一个的情况所以react默认提供的代码比较复杂if(pre.name cur.name) {return true;}return false;
});const App (){const [state, setState] useState(0);return (div classNameAppbutton onClick{()setState(state1)}点我看看子组件刷新了吗/buttonMemoIndex name{0}//div);
}
父组件传了函数
const Index () {console.log(子组件刷新了);return (div这是子组件/div)
}
const MemoIndex React.memo(Index);const App (){const [state, setState] useState(0);const func () {};return (div classNameApp//这时候子组件会不会刷新呢有的同学可能说不会因为浅比较发现pre.func cur.func 返回//true所以不会刷新但是其实是会刷新的因为APP组件中触发了setState之后App组件重新渲染也//就是相当于执行了App()这个方法所以里面func的指向地址发生了变化所以pre.func ! //cur.func 子组件会重新渲染button onClick{()setState(state1)}点我看看子组件刷新了吗/buttonMemoIndex func{func}//div);
}使用useCallback缓存函数
const Index () {console.log(子组件刷新了);return (div这是子组件/div)
}const MemoIndex React.memo(Index);const App (){const [state, setState] useState(0);//这里我们用了useCallbackuseCallback主要是缓存我们当前的函数如果我们第二个参数传递空数组的话//他的地址不会改变如果我们第二个参数传递的是一个变量这个变量发生变化他的地址就会发生变化。所以这//和useEffect的第二个参数是一样的但是请注意不要滥用useCallback的第二个参数。如果第二个参数滥用//会拿到我们之前的值。我们看下一个示例就知道了const func useCallback(() {}, [])return (div classNameAppbutton onClick{()setState(state1)}点我看看子组件刷新了吗/buttonMemoIndex func{func}//div);
}
使用useMemo缓存组件
useMemo不仅可以缓存变量函数还可以缓存组件
const Index (props) {console.log(子组件刷新了);return (div这是子组件/div)
}const App (){const [state, setState] useState(0);//使用useMemo也要和useCallback一样特别注意第二个参数因为他有可能导致我们拿不到最新的数据解决//解决方案就和useCallback的一样简单来说套用react官方的话就是请确保数组中包含了所有外部作用域//中会随时间变化并且在useMemo中使用的变量都要放到第二个参数中。const Component useMemo(()Index/, []);return (div classNameAppbutton onClick{()setState(state1)}点我看看子组件刷新了吗/button{Component}/div);
}
利用props.children缓存组件
这样在Index组件re-render的时候由于App父组件中的组件没有变化所以拿到的children依然是上一次的没有发生变化的所以children部分不会re-render。
const Index (props) {const [state, setState] useState(0);return (div//当这个按钮点击之后我们发现Children组件并不重新刷新了其实原理我理解的是react帮我们做了一层//处理当渲染前与渲染后两个组件的引用地址一样他就会放弃render当然这是我的猜测这个的话之后//我看到源码的时候会和大家讲一下在补充一下。button onClick{()setState(state1)}点我我看看子组件刷新不刷新/button{props.children}/div)
};
const Children () {return (div{console.log(子组件刷新了)}这是children组件/div)
}const App (){return (div classNameAppIndexChildren//Index/div);
}注意事项
useCallBack不是每个函数都需要使用不要滥用useCallback
const Index (props) {console.log(子组件刷新了);return (div//点击这个按钮之前先点击App组件下面的按钮让state变大然后在点击这个按钮看看state是啥//我们发现state一直是一个0。这是为什么呢因为很简单我们之前讲了useCallback第二个和//useEffect的第二个参数是一样的因为我们传递的是空数组说以useCallback一直拿到的是最原始的//值所以会造成这个问题我们写代码的时候千万要注意第二个参数只要useCallback需要什//值我们就在第二个参数传递什么值这样才可以确保我们拿到的是最新的值。同样的里面如果不需要一些//参数的话我们也不要把这些参数加到第二个参数上面否则会出现func的地址多次改变。button onClick{props.func}点我看看state是啥/button这是子组件/div)
}
const MemoIndex React.memo(Index);const App (){const [state, setState] useState(0);const func useCallback(() {console.log(state);}, [])return (div classNameAppbutton onClick{()setState(state1)}点我看看子组件刷新了吗/buttonMemoIndex func{func}//div);
}
useCallBack是一个缓存工具没错。但实际上他并不能阻止函数都重现构建。
示例 大家看上方这种结构的组件,Com组件中包含了fun1和fun2两个函数。
是不是认为当Com组件重新渲染的时候只有fun2没有使用useCallBack的函数函数会被重新构建而fun1使用了useCallBack的函数函数不会被重新构建。
实际上被useCallBack包裹了的函数也会被重新构建并当成useCallBack函数的实参传入。 useCallBack的本质工作不是在依赖不变的情况下阻止函数创建而是在依赖不变的情况下不返回新的函数地址而返回旧的函数地址。不论是否使用useCallBack都无法阻止组件render时函数的重新创建
每一个被useCallBack的函数都将被加入useCallBack内部的管理队列。而当我们大量使用useCallBack的时候管理队列中的函数会非常之多任何一个使用了useCallBack的组件重新渲染的时候都需要去遍历useCallBack内部所有被管理的函数找到需要校验依赖是否改变的函数并进行校验。
在以上这个过程中寻找指定函数需要性能校验也需要性能。所以滥用useCallBack不但不能阻止函数重新构建还会增加“寻找指定函数和校验依赖是否改变”这两个功能为项目增添不必要的负担。
//Com组件
const Com () {//示例1包裹了useCallBack的函数const fun1 useCallBack(() {console.log(示例一函数);...},[])//示例2没有包裹useCallBack的函数const fun2 () {console.log(示例二函数);...}return div/div
}不要过度缓存组件
其实不必过度优化代码 react官方没有帮你做 其实也证明了 如果你的代码没有明显的卡顿 你自己去做优化 可能造成负优化
优化的手段一般都是针对组件本身比较复杂且数据量大每次re-render都会造成卡顿的情况下才去做的没有太明显的卡顿出现时没必要做这些优化。而且在使用memo前其实也有手段去规避这些无效的re-render比如将组件粒度划分的更细一些
参考文章
useCallBack你真的知道怎么用吗。