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.0useReducer
buggy示例中的钩子的)是否正确
步骤
道具更改时,使用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