面试题答案
一键面试策略一:使用 React.memo
包裹消费组件
React.memo
是一个高阶组件,它通过浅比较 props 来决定组件是否需要重新渲染。如果 props 没有变化,组件将不会重新渲染。
import React from 'react';
// 创建Context
const MyContext = React.createContext();
// 消费组件,使用React.memo包裹
const MyConsumer = React.memo(({ value }) => {
return <div>{value}</div>;
});
const MyProvider = () => {
const [count, setCount] = React.useState(0);
return (
<MyContext.Provider value={count}>
<MyConsumer />
<button onClick={() => setCount(count + 1)}>Increment</button>
</MyContext.Provider>
);
};
export default MyProvider;
在上述代码中,MyConsumer
组件使用 React.memo
包裹。只有当 MyContext
提供的 value
发生变化时,MyConsumer
才会重新渲染。如果 value
没有改变,即使父组件 MyProvider
重新渲染,MyConsumer
也不会重新渲染。
策略二:拆分 Context
如果 Context 包含多个部分的数据,将其拆分成多个 Context,这样只有相关部分数据变化时,对应的消费组件才会重新渲染。
import React from 'react';
// 创建两个Context
const ThemeContext = React.createContext();
const UserContext = React.createContext();
// 主题消费组件
const ThemeConsumer = React.memo(({ theme }) => {
return <div>{`Theme: ${theme}`}</div>;
});
// 用户消费组件
const UserConsumer = React.memo(({ user }) => {
return <div>{`User: ${user}`}</div>;
});
const App = () => {
const [theme, setTheme] = React.useState('light');
const [user, setUser] = React.useState('John');
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={user}>
<ThemeConsumer />
<UserConsumer />
<button onClick={() => setTheme(theme === 'light'? 'dark' : 'light')}>
Change Theme
</button>
<button onClick={() => setUser('Jane')}>Change User</button>
</UserContext.Provider>
</ThemeContext.Provider>
);
};
export default App;
这里,ThemeContext
和 UserContext
分别管理主题和用户信息。当主题变化时,只有 ThemeConsumer
会重新渲染;当用户信息变化时,只有 UserConsumer
会重新渲染,避免了不必要的整体重新渲染。
策略三:使用 useReducer
与 Context 结合
useReducer
可以将复杂的状态逻辑提取到一个 reducer 函数中,并且可以利用 reducer 的 action 来进行更细粒度的更新控制。
import React from'react';
// 创建Context
const CounterContext = React.createContext();
// reducer函数
const counterReducer = (state, action) => {
switch (action.type) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
default:
return state;
}
};
const CounterProvider = () => {
const [count, dispatch] = React.useReducer(counterReducer, 0);
return (
<CounterContext.Provider value={{ count, dispatch }}>
<CounterConsumer />
</CounterContext.Provider>
);
};
// 消费组件
const CounterConsumer = React.memo(({ count, dispatch }) => {
return (
<div>
<div>{count}</div>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
});
export default CounterProvider;
在这个例子中,CounterConsumer
组件通过 React.memo
包裹,并且状态更新通过 dispatch
触发特定的 action
。只有当 count
或 dispatch
函数本身发生变化时(在这种情况下,dispatch
函数引用不会变),CounterConsumer
才会重新渲染,有效避免了不必要的重新渲染。