如何在counter函数中实时获取useEffect内已更新的React状态值?
React Hooks状态更新后立即使用最新值的问题解决
问题描述
我知道React Hooks是异步的,所以用带依赖数组的useEffect能实时拿到计数器的更新值,但我需要在counter()函数而非useEffect里用这些值。目前useEffect里能正确打印更新后的liked和disliked,但counter()及关联的handleLike、handleDislike函数中,状态值要点击第二次才会更新,把liked和disliked作为参数传入counter函数也没用,相关代码如下:
const [liked, setLiked] = useState(0); const [disliked, setDisliked] = useState(0); useEffect(() => { // Correct values are being logged console.log("liked", liked); console.log("disliked", disliked); }, [liked, disliked]); const counter = () => { if(someCondition.........) { setDisliked(disliked + 1); handleLike(); } else { setLiked(liked + 1); handleDislike(); } }; const handleLike = () => { const url = 'http://127.0.0.1:8000/api/like'; const headers = { "Accept": 'application/json' }; let data = { 'liked' : liked // value updates on second click only }; console.log(data); axios.post(url, data, {headers}) .then(resp => { console.log(resp.data); }).catch(err => { console.log(err); }); }; const handleDislike = () => { const url = 'http://127.0.0.1:8000/api/dislike'; const headers = { "Accept": 'application/json' }; let data = { 'disliked' : disliked // value updates on second click only }; console.log(data); axios.post(url, data, {headers}) .then(resp => { console.log(resp.data); }).catch(err => { console.log(err); }); }; return( <button onClick={counter}>Click Here</button> );
解决方案
方案1:函数式更新+直接传递最新值
利用ReactsetState的函数式更新特性,获取当前最新状态并计算新值,然后直接把新值传给handleLike/handleDislike,避开闭包捕获旧值的问题:
const [liked, setLiked] = useState(0); const [disliked, setDisliked] = useState(0); useEffect(() => { console.log("liked", liked); console.log("disliked", disliked); }, [liked, disliked]); const counter = () => { if (someCondition) { setDisliked(prevDisliked => { const newDisliked = prevDisliked + 1; handleLike(newDisliked); return newDisliked; }); } else { setLiked(prevLiked => { const newLiked = prevLiked + 1; handleDislike(newLiked); return newLiked; }); } }; const handleLike = (newDisliked) => { const url = 'http://127.0.0.1:8000/api/like'; const headers = { "Accept": 'application/json' }; let data = { 'liked': newDisliked }; console.log(data); axios.post(url, data, { headers }) .then(resp => console.log(resp.data)) .catch(err => console.log(err)); }; const handleDislike = (newLiked) => { const url = 'http://127.0.0.1:8000/api/dislike'; const headers = { "Accept": 'application/json' }; let data = { 'disliked': newLiked }; console.log(data); axios.post(url, data, { headers }) .then(resp => console.log(resp.data)) .catch(err => console.log(err)); }; return <button onClick={counter}>Click Here</button>;
方案2:用useRef保存最新状态
通过useRef实时同步状态的最新值,这样在任何函数里都能直接拿到最新的状态值:
const [liked, setLiked] = useState(0); const [disliked, setDisliked] = useState(0); const likedRef = useRef(liked); const dislikedRef = useRef(disliked); // 状态更新时同步ref的值 useEffect(() => { likedRef.current = liked; dislikedRef.current = disliked; console.log("liked", liked); console.log("disliked", disliked); }, [liked, disliked]); const counter = () => { if (someCondition) { setDisliked(prev => prev + 1); handleLike(); } else { setLiked(prev => prev + 1); handleDislike(); } }; const handleLike = () => { const url = 'http://127.0.0.1:8000/api/like'; const headers = { "Accept": 'application/json' }; // 直接从ref取最新值+1,对应set后的结果 let data = { 'liked': dislikedRef.current + 1 }; console.log(data); axios.post(url, data, { headers }) .then(resp => console.log(resp.data)) .catch(err => console.log(err)); }; const handleDislike = () => { const url = 'http://127.0.0.1:8000/api/dislike'; const headers = { "Accept": 'application/json' }; let data = { 'disliked': likedRef.current + 1 }; console.log(data); axios.post(url, data, { headers }) .then(resp => console.log(resp.data)) .catch(err => console.log(err)); }; return <button onClick={counter}>Click Here</button>;
问题原因
React状态更新是异步的,而且组件中的函数会形成闭包。handleLike和handleDislike定义时会捕获当时的liked/disliked值,调用setLiked/setDisliked后状态不会立刻更新,所以此时函数里的状态还是旧值,要等下一次组件渲染(第二次点击)才能拿到新值。
内容的提问来源于stack exchange,提问作者sp92
相关产品推荐
相关产品推荐

