Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/22.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
Javascript 当Reducer函数依赖于组件属性时,传递给useReducer hook的Reducer函数会针对一个分派调用执行多次_Javascript_Reactjs_React Hooks - Fatal编程技术网

Javascript 当Reducer函数依赖于组件属性时,传递给useReducer hook的Reducer函数会针对一个分派调用执行多次

Javascript 当Reducer函数依赖于组件属性时,传递给useReducer hook的Reducer函数会针对一个分派调用执行多次,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,我正在阅读一篇名为“”的文章,并试图实现“”部分中的示例 在该示例中,有一个计数器组件,它在useReducerhook的帮助下定义状态(只是一个数字)。Reducer只处理一个操作-“tick”,在该操作上,它通过stepprop的值来增加状态'tick'操作在间隔函数中每秒调度一次,间隔函数在useffecthook中设置一次。 下面是该示例中的代码,其中有一些小的修改: 函数计数器({step}){ const[count,dispatch]=React.useReducer(reduce

我正在阅读一篇名为“”的文章,并试图实现“”部分中的示例

在该示例中,有一个
计数器
组件,它在
useReducer
hook的帮助下定义状态(只是一个数字)。Reducer只处理一个操作-
“tick”
,在该操作上,它通过
step
prop的值来增加状态<代码>'tick'操作在间隔函数中每秒调度一次,间隔函数在
useffect
hook中设置一次。
下面是该示例中的代码,其中有一些小的修改:

函数计数器({step}){
const[count,dispatch]=React.useReducer(reducer,0);
功能减速机(状态、动作){
如果(action.type==“勾选”){
log(`Reducer:state=${state}和step=${step}`);
返回状态+步进;
}否则{
抛出新错误(`Unknown action type:${action.type}`);
}
}
React.useffect(()=>{
日志(“创建间隔”);
常量id=setInterval(()=>{
控制台日志(“调度”);
分派({type:“tick”});
}, 1000);
return()=>{
控制台日志(“清除间隔”);
清除间隔(id);
};
},[发送];
返回{count};
}
函数App(){
const[step,setStep]=React.useState(0);
返回(
setStep(Number(e.target.value))}
/>
);
}
我发现这个例子适用于
react@16.8.0-alpha.0
且不打开
react@16.8.0
及更高版本。运行代码时,步长和计数器的初始值均为
0
。如果我等待3秒钟而不做任何更改,然后增加步长,我将得到以下输出:

Create interval
Dispatch
Reducer: state=0 and step=0
Dispatch
Reducer: state=0 and step=0
Dispatch
Reducer: state=0 and step=0
Reducer: state=0 and step=1
Reducer: state=1 and step=1
Reducer: state=2 and step=1
Dispatch
Reducer: state=3 and step=1
Reducer: state=3 and step=1
Dispatch
Reducer: state=4 and step=1
Dispatch
Reducer: state=5 and step=1
正如您从日志中看到的,reducer的执行次数超过了
“tick”
操作的调度次数

通过从
step
prop创建一个ref,并使用
useCallback
hook在没有任何依赖项的情况下记忆减速器,我已成功地使其按预期工作

const stepRef=React.useRef(步骤);
React.useffect(()=>{
步进参考电流=步进;
},[步骤];
const reducer=useCallback((状态、操作)=>{
如果(action.type==“勾选”){
log(`Reducer:state=${state}和step=${stepRef.current}`);
返回状态+步进参考电流;
}否则{
抛出新错误(`Unknown action type:${action.type}`);
}
}, []);
您可以在此处使用以下示例:

  • (如果将React版本更改为
    react@16.8.0-alpha.0
    它将按预期工作)
  • (两个版本都适用)
但问题依然存在

  • 这些行为是什么(
    react@16.8.0-alpha.0
    react@16.8.0
    useReducer
    buggy示例中的钩子的
    )是否正确
  • 是虫子吗
  • 如果它不是一个bug,那么为什么它是这样工作的,而reducer被触发的次数超过了需要
  • 最后一个问题的答案应该在某种程度上与减速器正在重新创建这一事实有关。无论是在每次渲染时,还是仅当
    步骤
    道具更改时,使用
    useCallback
    钩子记忆减速器并将
    [step]
    作为依赖项数组传递都不会解决问题。有人对此有什么想法吗

    谢谢

    从“React”导入React;
    
    import React from "react";
    import ReactDOM from "react-dom";
    
    function App() {
      const [step, setStep] = React.useState(0);
    
      function Counter({ step }) {
        const [count, dispatch] = React.useReducer(reducer, 0);
    
        function reducer(state, action) {
          if (action.type === "tick") {
            console.log(`Reducer: state=${state} and step=${step}`);
            return step === 0 ? state : state + step;
          } else {
            throw new Error(`Unknown action type: ${action.type}`);
          }
        }
    
        React.useEffect(() => {
          console.log("Create interval");
          const id = setInterval(() => {
            console.log("Dispatch");
            dispatch({ type: "tick" });
          }, 1000);
          return () => {
            console.log("Clear interval");
            clearInterval(id);
          };
        }, [dispatch]);
    
        return <h1>{count}</h1>;
      }
    
      return (
        <>
          <Counter step={step} />
          <input
            type="number"
            value={step}
            onChange={(e) => setStep(Number(e.target.value))}
          />
        </>
      );
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    
    从“react dom”导入react dom; 函数App(){ const[step,setStep]=React.useState(0); 函数计数器({step}){ const[count,dispatch]=React.useReducer(reducer,0); 功能减速机(状态、动作){ 如果(action.type==“勾选”){ log(`Reducer:state=${state}和step=${step}`); 返回步骤===0?状态:状态+步骤; }否则{ 抛出新错误(`Unknown action type:${action.type}`); } } React.useffect(()=>{ 日志(“创建间隔”); 常量id=setInterval(()=>{ 控制台日志(“调度”); 分派({type:“tick”}); }, 1000); return()=>{ 控制台日志(“清除间隔”); 清除间隔(id); }; },[发送]; 返回{count}; } 返回( setStep(Number(e.target.value))} /> ); } const rootElement=document.getElementById(“根”); render(,rootElement);
    useReducer
    需要记住
    reducer
    ,以便能够说出组件是否需要重新渲染器(需要它计算状态并与前一个进行比较)。但是它可以访问的减速器可能已经过时,因为您可以“交换”减速器。因此,在减速器状态与前一个状态相同的情况下,React会将结果以及用于计算结果的减速器隐藏到下一次重新渲染,然后检查减速器是否仍然相同,而不是将结果丢弃并称之为“一天”。如果不是,则使用新的减速器再次计算状态

    您的示例是一个edge案例。它不应该像那样工作,但是Ract不知道什么时候应该丢弃过时的reducer状态更新-它总是等到下一个重新渲染器比较reducer并计算将在重新渲染中使用的最终状态

    下面是一个简单的示例: 最初
    步骤
    0
    <代码>调度
    运行3次。结果是
    0+0+0=0
    这是正确显示的。然后,当您单击按钮时,
    步骤
    变为
    1
    ,但
    调度
    甚至不会触发一次。然而,现在的结果是
    3
    ,因为以前的所有操作都是使用新创建的减速器重新运行的

    函数应用程序(){
    console.log(“rend