Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ssis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Reactjs 反应挂钩,提供上下文,但不要';t在状态更改时重新启用子项_Reactjs_React Hooks - Fatal编程技术网

Reactjs 反应挂钩,提供上下文,但不要';t在状态更改时重新启用子项

Reactjs 反应挂钩,提供上下文,但不要';t在状态更改时重新启用子项,reactjs,react-hooks,Reactjs,React Hooks,我的目标是能够有一个计时器,孩子们可以访问。问题是,只有少数组件需要秒值,但许多组件需要能够操作计时器(暂停计时器、更改计时器值、重置计时器等)。我的解决方案是将所有的子项都封装在其中,但访问它的子项仍然会在每次第二次更新时呈现出来——即使我没有在上下文中解包第二次更新 这是包装纸: import * as React from 'react'; import Timer, {ITimerSettings} from '../../utilities/Timer'; export const

我的目标是能够有一个计时器,孩子们可以访问。问题是,只有少数组件需要秒值,但许多组件需要能够操作计时器(暂停计时器、更改计时器值、重置计时器等)。我的解决方案是将所有的子项都封装在其中,但访问它的子项仍然会在每次第二次更新时呈现出来——即使我没有在上下文中解包第二次更新

这是包装纸:

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);

我的问题是,为什么每次秒更新时子对象都会重新渲染,我如何防止它?我是否需要拆分上下文,以便有一个用于秒,一个用于计时器?

上下文值在每次渲染时都是一个新对象,因此每次第二次更新时都是如此

<TimerProvider value={ {seconds, timer, setTimer} }>

尝试直接传递timer ref,但不要传递
timer.current
,因为
timer.current
在每次定时器更新时都有一个新值(如果我理解正确)并尝试在引用中存储秒数。你可能不想在每个计时器上更新任何道具。好主意-我尝试只传递引用并使用timer.current访问它,但这似乎不起作用。这似乎起作用-将getter和setter分为两个不同的上下文!唯一让我感到困惑的是,为什么控件需要进入它们自己的状态?我认为使用ref作为计时器,使用callback作为setTimer函数是没有必要的?我认为
是关键。每次都有一个新对象的值,即使使用者只分解出未更改的
计时器
实例,当
seconds
值导致其更新时,包含它的匿名对象已更改。我询问状态的原因是我首先尝试了单独的提供程序,但没有使用控件实例部分(我只是直接传入了计时器和setTimer),并且它不起作用。但当我添加useState时,它起了作用。
<TimerProvider value={ {seconds, timer, setTimer} }>
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);