react Hook 已成为当前最流行的开发范式,React 16.8 以后基于 Hook 开发极大简化开发者效率,同时不正确的使用 React Hook也带来了很多的性能问题,本文梳理基于 React Hook 开发组件的过程中如何提高性能。
每次点击 Increase 都会引起子组件 Child 的渲染,哪怕子组件并没有状态变化:
function Before(){
console.log('Demo1 Parent')
let [count,setCount] = useState(0)
let [name,setName] = useState('-')
const handleClick = ()=>{
setCount(count+1)
}
const handleInput = (e)=>{
setName(e.target.value)
}
return (
<div>
<div className='l50'>
<label>计数器:</label>
<span className='mr10'>{count}</span>
<button className='ml10' onClick={handleClick}>Increase</button>
</div>
<div className='l50'>
<label htmlFor="">改变子组件:</label>
<input type="text" onChange={handleInput}/>
</div>
<hr />
<Child name={name}/>
</div>
)
}
// 子组件
function Child(props){
console.log('Demo1 Child')
return (
<div className='l50'>
子组件渲染:{props.name}
</div>
)
}
只需要把 Increase 抽取成独立的组件即可。此时点击按钮,子组件并不会渲染。
/**
* 优化后,Increase提取以后,上下文发生变化,组件内
* @returns
*/
function Increase(){
console.log('Child Increase')
let [count,setCount] = useState(0)
const handleClick = ()=>{
setCount(count+1)
}
return (
<div>
<div className='l50'>
<label>计数器:</label>
<span className='mr10'>{count}</span>
<button className='ml10' onClick={handleClick}>Increase</button>
</div>
</div>
)
}
function After(){
console.log('Demo1 Parent')
let [name,setName] = useState('-')
const handleInput = (e)=>{
setName(e.target.value)
}
return (
<div>
<Increase/>
<div className='l50'>
<label htmlFor="">改变子组件:</label>
<input type="text" onChange={handleInput}/>
</div>
<Child name={name}/>
</div>
)
}
// 子组件
function Child(props){
console.log('Demo1 Child')
return (
<div className='l50'>
子组件渲染:{props.name}
</div>
)
}
同样基于上述优化前代码,即使不抽取组件,使用 memo 优化后,当点击按钮时,也不会触发二次渲染。
// 优化前
function AfterMemo(){
console.log('Demo1 Parent')
let [count,setCount] = useState(0)
let [name,setName] = useState('-')
const handleClick = ()=>{
setCount(count+1)
}
const handleInput = (e)=>{
setName(e.target.value)
}
return (
<div>
<div className='l50'>
<label>计数器:</label>
<span className='mr10'>{count}</span>
<button className='ml10' onClick={handleClick}>Increase</button>
</div>
<div className='l50'>
<label htmlFor="">改变子组件:</label>
<input type="text" onChange={handleInput}/>
</div>
<Child name={name}/>
</div>
)
}
// 子组件
const Child = memo((props)=>{
console.log('Demo1 Child')
return (
<div className='l50'>
子组件渲染:{props.name}
</div>
)
})
React.memo 为高阶组件,与 React.PureComponent相似。
function TestComponent(props){
// 使用 props 渲染
}
function areEqual(prevProps,nextProps){
/*
如果把 nextProps 传入 render 方法的返回结果与
将 prevProps 传入 render 方法的返回结果一致则返回 true,
否则返回 false
*/
}
export default React.memo(TestComponent,areEqual)
与 class 组件中 shouldComponentUpdate() 方法不同的是,如果 props 相等,areEqual 会返回 true;如果 props 不相等,则返回 false。这与 shouldComponentUpdate 方法的返回值相反。
如果已经用了 memo ,当遇到下面这种场景时,同样会触发子组件渲染。比如,给 Child 绑定一个 handleClick ,子组件内部增加一个按钮,当点击子组件的按钮时,更改 count 值,即使没有发生 name 变化,也同样会触发子组件渲染,为什么?memo 不是会判断 name 变化了,才会更新吗?
function Before(){
console.log('Demo1 Parent')
let [count,setCount] = useState(0)
let [name,setName] = useState('-')
const handleClick = ()=>{
setCount(count+1)
}
const handleInput = (e)=>{
setName(e.target.value)
}
const handleChange = ()=>{
setCount(count+1)
}
return (
<div>
<div className='l50'>
<label>计数器:</label>
<span className='mr10'>{count}</span>
<button className='ml10' onClick={handleClick}>Increase</button>
</div>
<div className='l50'>
<label htmlFor="">改变子组件:</label>
<input type="text" onChange={handleInput}/>
</div>
<Child name={name} handleClick={handleChange}/>
</div>
)
}
// 子组件
const Child = memo((props)=>{
console.log('Demo1 Child')
return (
<div className='l50'>
子组件渲染:{props.name}
<button onClick={props.handleClick}>更改count</button>
</div>
)
})
并不是 memo 没有生效,是因为当状态发生变化时,父组件会重新执行,导致重新创建了新的handleChange 函数,而 handleChange 的变化导致了子组件的再次渲染。
点击父组件的Increase按钮,更改了 count 值,经过 useCallback 包裹 handleChange 函数以后,我们会发现子组件不再渲染,说明每当父组件执行的时候,并没有创建新的 handleChange 函数,这就是通过 useCallback 优化后的效果。即使我们点击子组件的按钮,也同样不会触发子组件的渲染,同样 count 会进行累加。
function After(){
console.log('Demo1 Parent')
let [count,setCount] = useState(0)
let text = useRef();
let [name,setName] = useState('-')
const handleClick = ()=>{
setCount(count+1)
}
const handleInput = (e)=>{
setName(e.target.value)
}
const handleChange = useCallback(()=>{
// 为了让 count 能够累加,我们使用ref 获取值
let val = parseInt(text.current.textContent);
setCount(val+1)
},[])
return (
<div>
<div className='l50'>
<label>计数器:</label>
<span className='mr10' ref={text}>{count}</span>
<button className='ml10' onClick={handleClick}>Increase</button>
</div>
<div className='l50'>
<label htmlFor="">改变子组件:</label>
<input type="text" value={name} onChange={handleInput}/>
</div>
<Child name={name} handleClick={handleChange}/>
</div>
)
}
// 用法
useCallback(()=>{
// to-do
},[])
// 示例
function App(){
// 点击按钮调用此函数,但返回被缓存
const onClick = useCallback(() => {
console.log('我被缓存了,怎么点击都返回一样');
}, []);
return (
<button onClick={onClick}>点击</button>
);
}
我们定义了一个total函数,内部使用 1 填充了100次,通过 reduce 计算总和,经过测试发现点击 Increase按钮后,只会执行 total1 ,不会执行 total2,假设total计算量巨大,就会造成内存的浪费,通过 useMemo 可以帮我们缓存计算值。
function Before(){
console.log('Demo1 Parent')
let [count,setCount] = useState(0)
const handleClick = ()=>{
setCount(count+1)
}
const total1 = ()=>{
console.log('计算求和1')
let arr = Array.from({ length:100 }).fill(1)
return arr.reduce((prev,next)=>prev+next,0)
}
// 缓存对象值
const total2 = useMemo(()=>{
console.log('计算求和2')
let arr = Array.from({ length:100 }).fill(1)
return arr.reduce((prev,next)=>prev+next,0)
},[count])
return (
<div>
<div className='l50'>
<label>计数器:</label>
<span className='mr10'>{count}</span>
<button className='ml10' onClick={handleClick}>Increase</button>
</div>
<div>
<label>总和:</label>
<span>{total1()}</span>
<span>{total2}</span>
</div>
</div>
)
}
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
传入一个函数进去,会返回一个memoized 值,需要注意的是,函数内必须有返回值
第二个参数会依赖值,当依赖值更新时,会重新计算。
他们都用于缓存,useCallback 主要用于缓存函数,返回一个 缓存后 函数,而 useMemo 主要用于缓存值,返回一个缓存后的值。
以上就是本次给大家整理的集中常见的组件优化,当然合理使用useEffect useLayout希望对不了解的朋友有用。
Vue.js 是我使用的第一个 JavaScript 框架。 我可以说 Vue.js 是我进入 JavaScript 世界的第一扇门之一。 目前,Vue.js 仍然是一个很棒的框架。 我认为有了组合 API,Vue.js 只会增长得更多
conftest.py可以作为最简单的本地plugin调用一些hook函数,以此来做些强化功能。pytest整个框架通过调用如下定义良好的hooks来实现配置,收集,执行和报告这些过程:
到 React 16.8 目前为止,如果编写函数组件,然后遇到需要添加状态的情况,咱们就必须将组件转换为类组件。编写 class Thing extends React.Component,将函数体复制到render()方法中,修复缩进,最后添加需要的状态。
所有这些新的React Hook之间都有一个宗旨:就是为了使函数组件像类组件一样强大。useContext hook 与其它几个有点不一样,但它在特定场景下还是很有用的。React 的 Context API 是一种在应用程序中深入传递数据的方法
我们在DidMount的时候通过ID订阅了好友的在线状态,并且为了防止内存泄漏,我们需要在WillUnmount清除订阅,但是当组件已经显示在屏幕上时,friend prop 发生变化时会发生什么?
useCallback和useMemo是其中的两个 hooks,本文旨在通过解决一个需求,结合高阶函数,深入理解useCallback和useMemo的用法和使用场景。 之所以会把这两个 hooks 放到一起说,是因为他们的主要作用都是性能优化
Hook是对函数式组件的一次增强,使得函数式组件可以做到class组件的state和生命周期。Hook的语法更加简练易懂,消除了class的生命周期方法导致的重复逻辑代码,解决了高阶组件难以理解和使用困难的问题。
最近因为使用列表展示的需求有点多,就想着把列表分页筛选的逻辑抽象一下。看了umi的一个useTable的hook,也不能满足业务需要,于是就自己写了一个,支持本地分页筛选和接口分页筛选。
然而实际上,由于回调函数被useCallback缓存,形成闭包,所以点击的效果始终是sendMessage()。这就是「闭包陷阱」。以上代码的一种解决方式是「为useCallback增加依赖项」
最近做需求,需要将数据保存到 localStorage 里,在组件初始化的时候获取,然后修改该值的时候,要保存到本地的 localStorage 中。很显然,这些逻辑完全可以封装为一个 React Hook
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!