react 父子组件的渲染机制 | 优化手段
本方法与组件形式引用的区别:组件重新渲染其实是执行render方法, 如果子组件采用组件形式引入(可以理解为这里引入的是子组件render方法的执行结果。但有些场景下我们并不希望所有的子组件都重新渲染,比如在一个列表中,我们只希望重新渲染单击受新选择影响的这项。有时无法轻易的把一个组件单独的独立提取出来,此时可以把带状态的组件提取出来,然后把耗时的组件作为。方法是一个高阶函数,参数是一个组件A,返
父子组件的渲染机制
渲染分初次渲染
和重新渲染
React组件会在两种情况下发生重新渲染
- 当组件自身的state发生变化
- 当组件的父组件重新渲染
当一个父组件被触发渲染时,其所有子组件都会重新渲染(子组件的子组件也会)。
但有些场景下我们并不希望所有的子组件都重新渲染,比如在一个列表中,我们只希望重新渲染单击受新选择影响的这项。
优化手段与实践写法
文章1::https://juejin.cn/post/7251861916146417723
父组件:下发state
在一个组件中,一部分组件使用了 state ,而另一部分组件和 state
相对孤立,此时可以将使用的了state
的组件拆分为子组件。
优化前
// 优化前写法
const Component = () => {
const [isOpen, setOpen] = useState(false)
return (
<div>
<button onClick={() => setOpen(!isOpen)}>open</button>
{ isOpen && <ModalDialog />}
{/* 状态的变化会引起 SlowComponent 重复渲染 */}
<SlowComponent />
</div>
)
}
优化后
优化思路:将使用了state
的组件拆分为一个子组件,state
在子组件中使用(将state
下发到子组件),state
变化时仅当前组件重渲染。
// 优化后写法
const Component = () => {
return (
<div>
<ButtonWithDialog />
<SlowComponent />
</div>
)
}
const ButtonWithDialog = () => {
const [isOpen, setOpen] = useState(false)
return (
<>
<button onClick={() => setOpen(!isOpen)}>open</button>
{ isOpen && <ModalDialog />}
</>
)
}
props.children 传递无状态组件
这两个方法其实思路都是一样的,就是拆分受state
影响的组件与不受state
影响的组件。
有时无法轻易的把一个组件单独的独立提取出来,此时可以把带状态的组件提取出来,然后把耗时的组件作为 props .children
传递。
优化前
const FullComponent = () => {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
};
return (
<div onClick={onClick} className="click-block">
<p>Click this component - "slow" component will re-render</p>
<p>Re-render count: {state}</p>
<VerySlowComponent />
</div>
);
};
优化后
优化思路:父组件传递props
对于引用类型来说其实传递是地址,也就是在子组件中使用props
引用类型其实是使用的地址值。执行父组件的render的时候,比较发现props.children
的引用地址没变化。
本方法与组件形式引用的区别:组件重新渲染其实是执行render方法, 如果子组件采用组件形式引入(可以理解为这里引入的是子组件render方法的执行结果。)
每次父组件重新渲染都会执行子组件的render方法获取新的执行结果。
const SplitComponent = () => {
return (
<>
<ComponentWithClick>
<>
<p>Click the block - "slow" component will NOT re-render</p>
<VerySlowComponent />
</>
</ComponentWithClick>
</>
);
};
const ComponentWithClick = ({ children }) => {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
};
return (
<div onClick={onClick} className="click-block">
<p>Re-render count: {state}</p>
{children}
</div>
);
};
props传递组件
该方法与props.children
本质是一样的,只不过有些时候如果无法通过props.children
传递,可以将组件作为props的参数传递。
优化前
const FullComponent = () => {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
};
return (
<div onClick={onClick} className="click-block">
<p>Click this component - "slow" component will re-render</p>
<p>Re-render count: {state}</p>
<VerySlowComponent />
<p>Something</p>
<AnotherSlowComponent />
</div>
);
};
优化后
优化思路:props
不受状态变化的影响,所以可以避免耗时组件的重复渲染。适用于耗时组件不受状态变化的影响,又不能作为 children
属性传递
const ComponentWithClick = ({ left, right }) => {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
};
return (
<div onClick={onClick} className="click-block">
<p>Re-render count: {state}</p>
{left}
<p>Something</p>
{right}
</div>
);
};
// 把组件作为 props 传递给组件,这样耗时组件就不受点击事件的影响
const SplitComponent = () => {
const left = (
<>
<h3>component with slow components passed as props</h3>
<p>Click the block - "slow" components will NOT re-render</p>
<VerySlowComponent />
</>
);
const right = <AnotherSlowComponent />;
return (
<>
<ComponentWithClick left={left} right={right} />
</>
);
};
React.memo缓存子组件与useCallback结合
React.memo
方法是一个高阶函数,参数是一个组件A,返回包装过的新组件B。
包装过的新组件B具有缓存功能,只有组件A的props
发生变化,才会触发组件重新渲染。
注意点
这里props
是浅比较,在将对象
和方法
作为 props
传递时必须考虑到引用地址的问题(如果地址变化,也会被认为props
变化了)。
解决办法
在父组件中,对于需要传递给子组件的引用类型
- 使用
useCallback
缓存函数 - 使用
useMemo
缓存函数返回的结果(本场景的作用是缓存对象)
比如选中的子组件高亮,父组件维护一个选中子组件的activeId
。
优化前写法:在子组件中对比当前Id
是否与activeId
一致。
点击子组件时,activeId
一直变化,所以每个子组件的props
会变化。
const children=({activeId,id})=>{
const isActive = activeId===id;
return (
<div className={isActive?'active':''}></div>
)
}
优化后写法
思路:缓存子组件,当props
变化时才渲染。在父组件判断当前子组件是否选中的,如果选中传递className
(这里可以自定义props,传递什么都行)。这样的好处是className
变化的子组件才会重新渲染。
// 在父组件中使用子组件
<Folder
className={activeId === item.id ? 'active' : ''}
key={item.id}
id={item.id}
/>
更多推荐
所有评论(0)