reactjs - useState() not updating inside of useEffect() hook
问题描述
I'm having a problem updating useState() inside of useEffect(). I think I may be running into an infinite loop and useState isn't updating regardless of the booleans that I pass in. It always shows false.
export const useAlert = () => {
const action = useSelector(state => state.edit.action);
const dispatch = useDispatch();
const booleanA = true;
const booleanB = false;
const [isOverMax, setIsOverMax] = useState(false);
const reset = () => {
dispatch(resetEditAction());
};
useEffect(() => {
setIsOverMax(booleanA || booleanB);
}, isOverMax);
return [isOverMax, reset];
};
For more context, booleanA
and booleanB
are dynamically generated with other code, but even when they are hardcoded like above, isOverMax
only ever is false
. I can never get a true value, even when both booleans are hardcoded as true.
解决方案
The state isOverMax
is derived from booleanA
and booleanB
. Thus isOverMax
should change whenever booleanA
or booleanB
is changed. You should reflect this in your dependency array.
useEffect(() => {
setIsOverMax(booleanA || booleanB);
}, [booleanA, booleanB]);
Here is the working example. Just click "Show code snippet" and then "Run code Snippet"
const useAlert = () => {
const [booleanA, setA] = React.useState(true);
const [booleanB, setB] = React.useState(false);
const [isOverMax, setIsOverMax] =React. useState(false);
const reset = () => {
setA(false);
setB(false);
};
React.useEffect(() => {
setIsOverMax(booleanA || booleanB);
}, [booleanA , booleanB]);
// just for testing
const toggleA = () => {
setA((a) => !a);
}
return [isOverMax, reset, toggleA];
};
const App = () => {
const [alert, reset, toggleA] = useAlert();
return <div>
isOverMax={"" + alert}<br/>
<button onClick={() => toggleA()} >Toggle A</button>
<button onClick={() => reset()} >Reset </button>
</div>;
};
ReactDOM.render(<App />, document.getElementById("root"));
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Since the states booleanA
and booleanB
already exist, they will trigger a rerender when one of them is changed. Thus you can just return the calculation of isOverMax
const useAlert = () => {
// ...
return [booleanA || booleanB, reset];
}
const useAlert = () => {
const [booleanA, setA] = React.useState(true);
const [booleanB, setB] = React.useState(false);
const isOverMax = booleanA || booleanB;
const reset = () => {
setA(false);
setB(false);
};
// just for testing
const toggleA = () => {
setA((a) => !a);
}
return [isOverMax, reset, toggleA];
};
const App = () => {
const [alert, reset, toggleA] = useAlert();
return <div>
isOverMax={"" + alert}<br/>
<button onClick={() => toggleA()} >Toggle A</button>
<button onClick={() => reset()} >Reset </button>
</div>;
};
ReactDOM.render(<App />, document.getElementById("root"));
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
EDIT
Are there any general guidelines for when NOT to use the effect hook so I don't start throwing it in code where it isn't crucial?
Not really, but you should have read Rules of Hooks. The rules tell you what you should do to prevent unpredictable behavior. Sadly they don't explain the reasons for that rule and this question is the wrong place to explain it all. Thus please open another question if you still have one.
推荐阅读
- javascript - 如何在 Vue.js 中访问 DOM
- rust - 便携式安装找不到 std 和 core 的 crate
- php - 使用带有 CURL 的 Geolocation API 获取纬度和经度时出错
- python - 具有多个估计器的 Sklearn 管道
- php - Laravel 使用条件表名对两个表进行连接查询
- python - 有没有办法从 yfinance 重命名数据框中的这些列?
- jquery - 如何在 mvc 视图中获取输入值?
- html - 我无法将位置粘性应用到子元素
- amazon-web-services - 使用 Make 文件为 Flutter 和 Golang 生成 gRPC 文件
- r - 从 R 中的 oracle 数据库读取数据的更快方法