reactjs - 反应钩子,提供上下文但不重新渲染状态变化的孩子
问题描述
我的目标是能够有一个孩子可以访问的计时器。问题是只有少数组件需要秒值,但许多组件需要能够操作计时器(暂停、更改值、重置等)。我的解决方案是将所有子项包装在其中,但访问它的子项仍会在每次第二次更新时呈现 - 即使我没有在上下文中解压缩秒数。
这是包装:
import * as React from 'react';
import Timer, {ITimerSettings} from '../../utilities/Timer';
export const TimerContext = React.createContext({
seconds: null,
setTimer: null,
timer: null
});
export const TimerProvider = TimerContext.Provider;
export const TimerConsumer = TimerContext.Consumer;
interface ITimerWrapperProps {
isPaused: boolean;
isReady: boolean;
children: any;
}
const TimerWrapper: React.FC<ITimerWrapperProps> = ({isReady, isPaused, children}) => {
const timer = React.useRef<Timer>(new Timer());
const [seconds, setSeconds] = React.useState<number>(null);
React.useEffect(() => {
if (isReady && timer.current && timer.current.duration) {
isPaused ? timer.current.stop() : timer.current.start();
}
}, [isReady, isPaused]);
const setTimer = React.useCallback((settings: Partial<ITimerSettings>): void => {
if (timer.current) {
timer.current.reset({
callback: i => setSeconds(i),
...settings
});
}
}, []);
return (
<TimerProvider value={{seconds, timer, setTimer}}>
{children}
</TimerProvider>
);
};
export default React.memo(TimerWrapper);
以下是孩子访问它的方式:
const {timer, setTimer} = React.useContext(TimerContext);
我的问题是,为什么孩子每次更新时都会重新渲染,我该如何防止呢?我是否需要拆分上下文,以便有一个用于秒数和一个用于计时器?
解决方案
上下文值是每次渲染的新对象,所以每次第二次更新
<TimerProvider value={ {seconds, timer, setTimer} }>
您希望使用秒数的组件更新seconds
值,而只使用控件的组件永远不会重新呈现。
我想我会把它分成 aTimerValueContext
和 a TimerControlsContext
。控件的值始终具有相同的实例。然后消费者可以选择一个或两个。
像这样的东西:(可能不工作的代码)
const TimerControlsContext = React.createContext({
setTimer: null,
timer: null
});
const TimerValueContext = React.createContext(0);
export const useTimerValue = () => {
const context = useContext(TimerValueContext);
if (context) return context;
throw new Error('Outside of provider!');
};
export const useTimerControls = () => {
const context = useContext(TimerControlsContext);
if (context) return context;
throw new Error('Outside of provider!');
};
interface ITimerWrapperProps {
isPaused: boolean;
isReady: boolean;
children: any;
}
const TimerWrapper: React.FC<ITimerWrapperProps> = ({isReady, isPaused, children}) => {
const timer = React.useRef<Timer>(new Timer());
const [seconds, setSeconds] = React.useState<number>(null);
React.useEffect(() => {
if (isReady && timer.current && timer.current.duration) {
isPaused ? timer.current.stop() : timer.current.start();
}
}, [isReady, isPaused]);
const setTimer = React.useCallback((settings: Partial<ITimerSettings>): void => {
if (timer.current) {
timer.current.reset({
callback: i => setSeconds(i),
...settings
});
}
}, []);
const [controlsInstance, _] = React.useState({timer, setTimer});
return (
<TimerControlsContext.Provider value={controlsInstance}>
<TimerValueContext.Provider value={seconds}>
{children}
<TimerValueContext.Provider>
</TimerControlsContext>
);
};
export default React.memo(TimerWrapper);
推荐阅读
- node.js - 我没有得到 jwt 令牌
- numpy - 计算两个numpy数组的向量之间的距离
- azure-keyvault - Spinnaker - Azure 密钥库集成
- wordpress - 创建自定义 WP 查询以获取自定义帖子类型
- javascript - 我想用 AJAX 从另一个域调用服务并得到 CORS 错误
- chatbot - 聊天机器人中的多个意图处理
- firebase - 如何使用问答应用 Flutter Firebase 设置我的数据库
- json - 如何为 Xero 构建自定义 Zapier 集成
- migration - Salesforce 和 Mulesoft 集成 || 读取 Excel 文件
- scala - 找出这个 Spark Scala 数据框的分组逻辑