上测试代码
// 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中做好控制。)