react面试之context的value变化时,内部所有子组件是否变化

上测试代码

// context
const state = {
    
    
    a: 1,
    b: 1,
}
const context = createContext(state);

export default context;
// A组件
const A = () => {
    
    
    const {
    
     a } = useContext(context);
    return (<div>{
    
    a}</div>)
}
export default A;

// B组件
const B = () => {
    
    
    const {
    
     b } = useContext(context);
    return (<div>{
    
    b}</div>)
}
export default B;

// C组件, 不引用context
const C = () => {
    
    
    return (<div>cccc</div>)
}
export default C;

// APP.js
function App() {
    
    
	const [a, setA] = useState(-1);
	return (
		<div>
			<Context.Provider value={
    
    {
    
    
				a,
				b
			}}>
	          <A />
	          <B />
	          <C />
	      	</Context.Provider>
	      	<button onClick={
    
    () => setA(a+1)}>+++</button>
      	</div>
     );

问,点击按钮时,ABC三个组件中谁会刷新?


答案是三个都会刷新,因为App组件中state改变了,导致所有组件都刷新了。所以并不能知道context有没有作用给B和C组件。

为了解决父组件state更新,而导致子组件更新,我们需要给ABC三个组件的export default加上memo方法,第二个参数为PropsAreEqual,直接让他返回true。

改造后为:

// A组件
// ...
export default memo(A, () => true);

// B组件
// ...
export default memo(B, () => true);

// C组件, 不引用context
// ...
export default memo(C, () => true);

此时,再次点击按钮,谁会触发更新。


答案是AB会触发更新,C不更新。

但是我们只更新了a的值,为什么没有使用a的B组件却也更新了呢?

是因为他们共同使用了一个context,而他的value是一个对象,每次都会重新生成一个新的对象,所以导致B组件以为context更新了,所以他就会触发渲染了。

所以可知,只要context的value发生改变,所有用到useContext(context)的都会更新。

如果我们在C组件里增加一个D组件,让D组件引用该context,会怎么样呢?

// C组件, 不引用context
const C = () => {
    
    
    return (<div>
	    cccc
	    <D/>
    </div>)
}
export default memo(C, () => true);
// D组件
const D = () => {
    
    
    const {
    
     b } = useContext(context);
    return (<div>{
    
    b}</div>)
}
export default memo(D, () => true);

点击按钮,C会触发更新么?

答案是不会,ABD都更新了,C没有更新。进一步验证了自己的猜想。

如果不想让BD因为a的改变而改变,该怎么办?

那就把b从state中拆解出来,重新创建一个bContext!

// App组件改为:
function App() {
    
    
const [a, setA] = useState(-1);
const [b, setB] = useState(-1);
	return (
		<div>
			<Context.Provider value={
    
    {
    
    a}}>
			  <BContext.Provider value={
    
    {
    
    b}}>
			    <A />
			    <B />
			    <C />
			  </BContext.Provider>
			</Context.Provider>
			<button onClick={
    
    () => setA(a+1)}>++a</button>
			<button onClick={
    
    () => setA(b+1)}>++b</button>
		</div>
	)
}
// B组件和D组件都更新为引用bContext
import bContext from "./context/b"
// ...
const {
    
     b } = useContext(bContext);
// ...

此时,你认为点击a按钮,BD会更新么?

答案是会更新,为什么?

因为BContext的value每次都是一个新对象,所以他认为一直在更改,所以如果想要传递对象,最好的办法就是在外层用useMemo重新定义一个。{a}同理。

// App改为
 const aVal = useMemo(() => ({
    
     a }), [a])
 const bVal = useMemo(() => ({
    
     b }), [b])
 
<Context.Provider value={
    
    aVal}>
	<BContext.Provider value={
    
    bVal}>
	   <A />
	   <B />
	   <C />
	 </BContext.Provider>
</Context.Provider>

此时更新a的值,并不会影响BD组件,只有A组件会更新。同时,更新b的值,也不会触发A组件的更新,只有BD更新。C则不会收到a,b两个值更新的影响。

至此,可以得出结论,context的值更新时,只会影响使用该context的组件,不会影响其他组件(前提是在memo中做好控制。)

猜你喜欢

转载自blog.csdn.net/qq_28992047/article/details/132073796