下面的示例是一个计时器组件,它有一个按钮(用于启动计时器)和两个标记,显示经过的秒数和经过的秒数乘以2。
但是,它不工作(CodeSandbox演示)
守则
import React, { useState, useEffect } from "react";
const Timer = () => {
const [doubleSeconds, setDoubleSeconds] = useState(0);
const [seconds, setSeconds] = useState(0);
const [isActive, setIsActive] = useState(false);
useEffect(() => {
let interval = null;
if (isActive) {
interval = setInterval(() => {
console.log("Creating Interval");
setSeconds((prev) => prev + 1);
setDoubleSeconds(seconds * 2);
}, 1000);
} else {
clearInterval(interval);
}
return () => {
console.log("Destroying Interval");
clearInterval(interval);
};
}, [isActive]);
return (
<div className="app">
<button onClick={() => setIsActive((prev) => !prev)} type="button">
{isActive ? "Pause Timer" : "Play Timer"}
</button>
<h3>Seconds: {seconds}</h3>
<h3>Seconds x2: {doubleSeconds}</h3>
</div>
);
};
export { Timer as default };
问题
在使用效果调用中,秒值将始终等于上次呈现使用效果块时(当isActive上次更改时)的值。这将导致setDouble秒(秒*2)语句失败。React Hooks ESLint插件给我一个关于这个问题的警告:
React Hook useEffect缺少依赖项:“秒”。包括它或删除依赖项数组。如果“setDoubleSeconds”需要当前值“seconds”,您还可以使用useReducer替换多个useState变量。(反应钩/详尽的deps)eslint
正确地说,向依赖数组添加“秒”(并将setDouble秒(秒*2)
更改为setDouble秒((秒1) * )
将呈现正确的结果。然而,这有一个令人讨厌的副作用,即导致间隔在每个渲染上被创建和破坏(console.log(“破坏间隔”)
在每个渲染上触发)。
因此,现在我在看ESLint警告中的另一条建议:“如果'setDoubleSeconds'需要当前的'seconds'值,您还可以用useReducer替换多个useStatehtml" target="_blank">变量。”。
我不理解这项建议。如果我创建一个减速器,并像这样使用它:
import React, { useState, useEffect, useReducer } from "react";
const reducer = (state, action) => {
switch (action.type) {
case "SET": {
return action.seconds;
}
default: {
return state;
}
}
};
const Timer = () => {
const [doubleSeconds, dispatch] = useReducer(reducer, 0);
const [seconds, setSeconds] = useState(0);
const [isActive, setIsActive] = useState(false);
useEffect(() => {
let interval = null;
if (isActive) {
interval = setInterval(() => {
console.log("Creating Interval");
setSeconds((prev) => prev + 1);
dispatch({ type: "SET", seconds });
}, 1000);
} else {
clearInterval(interval);
}
return () => {
console.log("Destroying Interval");
clearInterval(interval);
};
}, [isActive]);
return (
<div className="app">
<button onClick={() => setIsActive((prev) => !prev)} type="button">
{isActive ? "Pause Timer" : "Play Timer"}
</button>
<h3>Seconds: {seconds}</h3>
<h3>Seconds x2: {doubleSeconds}</h3>
</div>
);
};
export { Timer as default };
陈旧值的问题仍然存在(CodeSandbox演示(使用减缩器))。
问题
那么这个场景的推荐是什么呢?我是否接受性能打击,简单地向依赖数组添加“秒”?我是否创建了另一个依赖于"秒"并在其中调用"setDouble秒()"的use效应块?是否将“秒”和“双秒”合并到单个状态对象中?我使用裁判吗?
此外,您可能会想“为什么不简单地更改
谢谢
尽管我个人更愿意选择第二种方法,但它们都能正常工作:
useEffect(() => {
setDoubleSeconds(seconds * 2);
}, [seconds]);
然而:
在我的实际应用程序中,doubleSeconds被传递给子组件,我不希望子组件知道如何将seconds映射到doubleSeconds,因为这会降低子组件的可重用性
这是值得怀疑的。子组件可以实现如下:
const Child = ({second}) => (
<p>Seconds: {second}s</p>
);
和父组件应如下所示:
const [seconds, setSeconds] = useState(0);
useEffect(() => {
// change seconds
}, []);
return (
<React.Fragment>
<Child seconds={second} />
<Child seconds={second * 2} />
</React.Fragment>
);
这将是一种更加清晰和简洁的方式。
您可以通过几种方式访问效果回调中的值,而无需将其添加为dep。
设置状态
。您可以通过状态变量的setter点击状态变量的最新值
setSeconds(seconds => (setDoubleSeconds(seconds * 2), seconds));
const secondsRef = useRef(0);
const [seconds, setSeconds] = useReducer((_state, action) => (secondsRef.current = action), 0);
然后可以使用secondsRef。当前
访问代码块中的秒,而不触发deps更改。
setDoubleSeconds(secondsRef.current * 2);
在我看来,你永远不应该从deps数组中省略依赖项。如果你需要deps不改变,使用像上面这样的黑客来确保你的值是最新的。
首先要考虑的是,是否有一些更优雅的方式编写代码,而不是将值放入回调中。在您的示例中,doubleSeconds
可以表示为seconds
的导数。
const [seconds, setSeconds] = useState(0);
const doubleSeconds = seconds * 2;
有时候应用程序并不那么简单,所以你可能需要使用上面描述的黑客。
问题内容: 我正在尝试将事件发射器与React 和一起使用,但是它始终获取初始状态而不是更新状态。如果我直接调用事件处理程序(即使使用),也可以使用。 如果我将值传递给第二个参数,它将使其起作用,但是,每当值更改时(这是击键触发的),这都会导致事件发射器重新订阅。 我究竟做错了什么?我试过,,,和,并不能得到任何工作。 这是复制品: 这是具有相同代码的代码沙箱: https://codesandb
我一直在尝试创建一个,它也有一个处理程序。我阅读了这个问题的答案并知道如何做到这一点。 我的问题只是关于它的结束部分。 1) 我知道我可以写:或,但我不会写。在这里的注释中解释了这一点,因为您需要处理参数操作。 这是否意味着由于处理程序是
我正在使用驱动程序。findelements(By.xpath(“//*);要检索页面上的所有元素,但是在IE9/IE10上,我得到了一个陈旧的元素异常。我如何处理它?请帮助我。 错误如下所示:
在Autoconf的第2版,大部分宏被重新命名以使用更加统一和具有描述性的命名方案。下面是被重新命名了的宏的原来名字, 随后给出了这些宏现在的名字。虽然为了保持向后兼容,旧名字仍然能够被autoconf程序所接受,旧名字都 被看作过时的。关于新的命名方案,参见 宏名 。AC—ALLOCAAC—FUNC—ALLOCA AC—ARG—ARRAY 因为用途有限而被删除了。AC—CHAR—UNSIGNED
我们正在将AngularJS应用程序迁移到最新的Angular版本。我们总是通过接收状态代码200来处理http请求,如果有错误,我们仍然得到状态代码200(不包括404-500,以及关于实际服务器或用户连接的错误)。例如,如果用户令牌过期,我们发出请求,我们将得到一个状态代码200,其主体为errorcode:number,message:string。这是不会改变的。 使用关于令牌的相同示例,
主要内容:追踪Poll,其实真正处理响应是在 Networkclient的poll,步骤三追踪Poll 从poll里面进入slector的poll调用addToStagedReceives()进行消息处理(把接收的数据加入到待处理队列); 相应数据结构 selector的poll() addToCompletedReceives(),把响应存入到completedReceives 其实真正处理响应是在 Networkclient的poll,步骤三 调用 handleCompletedR