Dan*_*ick 34 javascript reactjs react-hooks
假设我有一些依赖于其他状态的状态(例如当A改变时我希望B改变).
是否适合创建一个观察A并在useEffect钩子中设置B的钩子?
效果是否级联,当我点击按钮时,第一个效果会激发,导致b改变,导致第二个效果在下一个渲染之前触发?构造这样的代码有任何性能缺点吗?
let MyComponent = props => {
let [a, setA] = useState(1)
let [b, setB] = useState(2)
useEffect(
() => {
if (/*some stuff is true*/) {
setB(3)
}
},
[a],
)
useEffect(
() => {
// do some stuff
},
[b],
)
return (
<button
onClick={() => {
setA(5)
}}
>
click me
</button>
)
}
Run Code Online (Sandbox Code Playgroud)
whi*_*rue 91
为了将来的目的,这也可能有帮助:
可以使用 setState 在useEffect你只需要注意已经描述的不要创建循环。
但这并不是可能发生的唯一问题。见下文:
想象一下,您有一个组件从父组件Comp接收props并根据props您想要设置Comp的状态的更改。出于某种原因,您需要为每个道具更改不同的useEffect:
不要这样做
useEffect(() => {
setState({ ...state, a: props.a });
}, [props.a]);
useEffect(() => {
setState({ ...state, b: props.b });
}, [props.b]);
Run Code Online (Sandbox Code Playgroud)
正如您在此示例中看到的那样,它可能永远不会改变 a 的状态:https : //codesandbox.io/s/confident-lederberg-dtx7w
在这个例子中发生这种情况的原因是因为当你改变两者时,两个 useEffects 在同一个反应周期中运行prop.a,prop.b所以{...state}你做的时候的值setState在两者中完全相同,useEffect因为它们处于相同的上下文中。当您运行第二个时setState,它将替换第一个setState。
改为这样做
这个问题的解决方案基本上setState是这样调用的:
useEffect(() => {
setState(state => ({ ...state, a: props.a }));
}, [props.a]);
useEffect(() => {
setState(state => ({ ...state, b: props.b }));
}, [props.b]);
Run Code Online (Sandbox Code Playgroud)
在此处查看解决方案:https : //codesandbox.io/s/mutable-surf-nynlx
现在,当您继续使用setState.
我希望这可以帮助别人!
小智 30
useEffect可以挂钩某个道具或状态。所以,你需要做的事情来避免无限循环钩子是绑定一些变量或状态来生效
例如:
useEffect(myeffectCallback, [])
Run Code Online (Sandbox Code Playgroud)
上述效果只会在组件渲染后触发。这类似于componentDidMount生命周期
const [something, setSomething] = withState(0)
const [myState, setMyState] = withState(0)
useEffect(() => {
setSomething(0)
}, myState)
Run Code Online (Sandbox Code Playgroud)
上面的效果只会触发我的状态改变了这类似于componentDidUpdate除了不是每个改变状态都会触发它。
您可以通过此链接阅读更多详细信息
Hos*_*rad 28
一般来说,使用setStateinside useEffect将创建一个无限循环,很可能是您不想引起的。该规则有几个例外,我将在以后讨论。
useEffect在每次渲染后调用,并在其中setState进行使用时,它将导致组件重新渲染,从而进行调用useEffect,依此类推。
使用useStateinside useEffect不会引起无限循环的一种常见情况是,当您将空数组作为第二个参数传递给useEffectlike时useEffect(() => {....}, []),这意味着效果函数应该被调用一次:仅在第一次安装/渲染之后。当您在组件中进行数据获取并且您想要以组件的状态保存请求数据时,此方法被广泛使用。
for*_*d04 20
? 1. 我可以在 useEffect 钩子中设置状态吗?
原则上,你可以自由设定的状态,你需要它-包括内部useEffect和即使在渲染。只要确保通过deps正确设置 Hook和/或有条件地状态来避免无限循环。
? 2. 假设我有一些状态依赖于其他状态。在 useEffect 钩子中创建一个观察 A 并设置 B 的钩子是否合适?
您刚刚描述了以下的经典用例useReducer:
useReduceruseState当您具有涉及多个子值的复杂状态逻辑或下一个状态取决于前一个状态时,通常更可取。(反应文档)当设置状态变量取决于另一个状态变量的当前值时,您可能想尝试将它们都替换为
useReducer. [...] 当您发现自己在编写 时setSomething(something => ...),现在是考虑使用 reducer 的好时机。(丹·阿布拉莫夫,反应过度的博客)
let MyComponent = () => {
let [state, dispatch] = useReducer(reducer, { a: 1, b: 2 });
useEffect(() => {
console.log("Some effect with B");
}, [state.b]);
return (
<div>
<p>A: {state.a}, B: {state.b}</p>
<button onClick={() => dispatch({ type: "SET_A", payload: 5 })}>
Set A to 5 and Check B
</button>
<button onClick={() => dispatch({ type: "INCREMENT_B" })}>
Increment B
</button>
</div>
);
};
// B depends on A. If B >= A, then reset B to 1.
function reducer(state, { type, payload }) {
const someCondition = state.b >= state.a;
if (type === "SET_A")
return someCondition ? { a: payload, b: 1 } : { ...state, a: payload };
else if (type === "INCREMENT_B") return { ...state, b: state.b + 1 };
return state;
}
ReactDOM.render(<MyComponent />, document.getElementById("root"));Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
<script>var { useReducer, useEffect } = React</script>Run Code Online (Sandbox Code Playgroud)
? 3. 效果是否会级联,当我点击按钮时,第一个效果会触发,导致 b 改变,导致第二个效果在下一次渲染之前触发?
useEffect始终在提交渲染并应用 DOM 更改后运行。第一个效果触发、改变b并导致重新渲染。此渲染完成后,第二个效果将由于b更改而运行。
let MyComponent = props => {
console.log("render");
let [a, setA] = useState(1);
let [b, setB] = useState(2);
let isFirstRender = useRef(true);
useEffect(() => {
console.log("useEffect a, value:", a);
if (isFirstRender.current) isFirstRender.current = false;
else setB(3);
return () => {
console.log("unmount useEffect a, value:", a);
};
}, [a]);
useEffect(() => {
console.log("useEffect b, value:", b);
return () => {
console.log("unmount useEffect b, value:", b);
};
}, [b]);
return (
<div>
<p>a: {a}, b: {b}</p>
<button
onClick={() => {
console.log("Clicked!");
setA(5);
}}
>
click me
</button>
</div>
);
};
ReactDOM.render(<MyComponent />, document.getElementById("root"));Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
<script>var { useReducer, useEffect, useState, useRef } = React</script>Run Code Online (Sandbox Code Playgroud)
? 4. 像这样构造代码有什么性能上的缺点吗?
是的。通过将 的状态变化包装b在单独的useEffectfor 中a,浏览器有一个额外的布局/绘制阶段 - 这些效果对用户来说可能是可见的。如果没有办法useReducer尝试,可以直接使用以下命令更改b状态a:
let MyComponent = () => {
console.log("render");
let [a, setA] = useState(1);
let [b, setB] = useState(2);
useEffect(() => {
console.log("useEffect b, value:", b);
return () => {
console.log("unmount useEffect b, value:", b);
};
}, [b]);
const handleClick = () => {
console.log("Clicked!");
setA(5);
b >= 5 ? setB(1) : setB(b + 1);
};
return (
<div>
<p>
a: {a}, b: {b}
</p>
<button onClick={handleClick}>click me</button>
</div>
);
};
ReactDOM.render(<MyComponent />, document.getElementById("root"));Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
<script>var { useReducer, useEffect, useState, useRef } = React</script>Run Code Online (Sandbox Code Playgroud)
Shu*_*tri 10
效果始终在渲染阶段完成后执行,即使您在一个效果中设置了setState,另一个效果也将读取更新的状态并仅在渲染阶段之后对其执行操作。
话虽如此,最好以相同的效果采取两种行动,除非有可能b由于其他原因而改变的可能性,changing a在这种情况下,您也希望执行相同的逻辑
| 归档时间: |
|
| 查看次数: |
23957 次 |
| 最近记录: |