父组件状态更新时如何避免子组件不必要的重复渲染?
问题根因
你当前的代码出现多余重渲染是两个原因共同导致的:
- 父组件
Home状态更新时,默认会重渲染所有嵌套在其JSX结构内的子组件,不管子组件是否依赖这个状态 - 传递给Context Provider的
value是每次渲染都新创建的对象,哪怕内部存储的字段值没有变化,引用地址的变更也会触发所有消费该Context的组件重渲染
修改方案
方法1:直接传递原始值到Context+用memo包裹子组件
1. 修改Home.js代码
import React, { useState, createContext } from "react"; import { User1, User2, User3 } from "./User"; export const UserContext = createContext(null); export const TypeContext = createContext(null); const Home = () => { const [user, setUser] = useState("User"); const [type, setType] = useState("Free"); return ( <> <input type="text" onChange={(event) => setUser(event.target.value)} /> <br /> <input type="text" onChange={(event) => setType(event.target.value)} /> {/* 去掉多余的对象包裹,直接传值 */} <UserContext.Provider value={user}> <User1 /> </UserContext.Provider> <TypeContext.Provider value={type}> <User2 /> </TypeContext.Provider> </> ); }; export default Home;
2. 修改User.js代码
import React, { useContext } from "react"; import { UserContext, TypeContext } from "./Home"; // 用React.memo包裹组件,阻止父组件重渲染带来的默认更新 export const User1 = React.memo(() => { // 直接取Context值,不需要解构 const user = useContext(UserContext); console.log("user1"); return <h1>Username is {user}</h1>; }); export const User2 = React.memo(() => { const type = useContext(TypeContext); console.log("user2"); return <h1>用户类型是 {type}</h1>; }); export const User3 = React.memo((props) => { console.log("user3"); return <h2>My Props {props.title}</h2>; });
方法2:如果需要在Context中传递多个值,用useMemo包裹Context的value
如果你的Context需要同时传递多个字段和方法,不能直接传原始值,就用useMemo保证只有依赖变化时才更新value的引用:
// Home.js 中新增useMemo处理 import React, { useState, createContext, useMemo } from "react"; // ...其他代码不变 const Home = () => { const [user, setUser] = useState("User"); const [type, setType] = useState("Free"); // 只有user变化时才会生成新的userContextValue const userContextValue = useMemo(() => ({ user, setUser }), [user]) // 只有type变化时才会生成新的typeContextValue const typeContextValue = useMemo(() => ({ type, setType }), [type]) return ( <> <input type="text" onChange={(event) => setUser(event.target.value)} /> <br /> <input type="text" onChange={(event) => setType(event.target.value)} /> <UserContext.Provider value={userContextValue}> <User1 /> </UserContext.Provider> <TypeContext.Provider value={typeContextValue}> <User2 /> </TypeContext.Provider> </> ); };
对应User.js的消费逻辑不用改,还是按原来的结构解构取值即可。
生效原理
React.memo会对组件接收的props做浅比较,User1和User2没有外部传入的props,父组件重渲染时props对比结果为无变化,就不会触发默认重渲染,只有组件依赖的Context变化时才会更新- 直接传原始值/用useMemo包裹Context value,保证只有Context存储的实际内容变化时,才会更新Context的引用,不会出现无意义的Context变更触发重渲染
内容的提问来源于stack exchange,提问作者Ankita Jaiswal
相关产品推荐
相关产品推荐

