反应useState不更新值
Posted
技术标签:
【中文标题】反应useState不更新值【英文标题】:react useState not updating value 【发布时间】:2020-10-28 14:38:27 【问题描述】:我正在使用 react typescript,我使用 useState
更新值,但该值没有在函数中更新,我使用了 const [isGameStarted, setIsGameStarted] = React.useState<any>('0');
,并且我正在更新它在 useEffect()
函数中的值,
React.useEffect(() =>
if(gameData?.game?.roundStarted)
if(isGameStarted == '0')
console.log("round is started");
setIsGameStarted('1');
, [gameData]);
这里我已将其值更新为 1,但对于我的 interval
函数,它没有更新该值,这里我提到了我的 interval
函数,此间隔函数每 1 秒调用一次,但它始终考虑 @987654330 @ value 为 0,任何人都可以帮助我为什么即使在 useEffect()
函数调用之后它没有得到 value 为 1,任何帮助都将不胜感激
const interval = () =>
let timer = setSeconds, minutes, seconds;
console.log("isGameStarted : "+isGameStarted);
if(isGameStarted == '0')
alert("0")
else
alert("1")
完整代码:
import Alert from "@material-ui/lab";
import Typography, useMediaQuery from "@material-ui/core";
import ShowWinner from "./ShowWinner";
import ErrorBoundary from "../../../../App/ErrorBoundary";
import GamePlayWhite from "../../GamePlayWhite";
import GamePlayBlack from "../../GamePlayBlack";
import GamePlaySpectate from "../../GamePlaySpectate";
import React, useEffect, useState from "react";
import useDataStore from "../../../../Global/Utils/HookUtils";
import GameDataStore from "../../../../Global/DataStore/GameDataStore";
import UserDataStore from "../../../../Global/DataStore/UserDataStore";
import IntervalDataStore from "../../../../Global/DataStore/IntervalDataStore";
import GameStart from "../../GameStart";
import GameJoin from "../../GameJoin";
import moment from "moment";
import ChatDataStore from "../../../../Global/DataStore/ChatDataStore";
import useHistory, useParams from "react-router";
import SiteRoutes from "../../../../Global/Routes/Routes";
import getTrueRoundsToWin from "../../../../Global/Utils/GameUtils";
import ClientGameItem from "../../../../Global/Platform/Contract";
import CurriedFunction1 from "lodash";
interface Props
gameId: string;
export const GameInner: React.FC<Props> = (
gameId,
) =>
const gameData = useDataStore(GameDataStore);
const userData = useDataStore(UserDataStore);
const chatData = useDataStore(ChatDataStore);
const params = useParams< throwaway?: string >();
const history = useHistory();
const [updateShowTimer, setUpdateShowTimer] = React.useState('02:00');
const [isCalled, setIsCalled] = React.useState<any>('0');
const [intervalData, setIntervalData] = useState(null as NodeJS.Timeout | null);
const [isGameStarted, setIsGameStarted] = React.useState<any>('0');
let setSeconds = 30;
const
dateCreated,
started,
chooserGuid,
ownerGuid,
spectators,
pendingPlayers,
players,
settings,
kickedPlayers
= gameData.game ?? ;
const
playerGuid
= userData;
const iWasKicked = !!kickedPlayers?.[playerGuid];
const amInGame = playerGuid in (players ?? );
useEffect(() =>
const playMode = params.throwaway !== "play" && started && !iWasKicked && amInGame;
const notPlayMode = iWasKicked && params.throwaway === "play";
if (playMode)
history.push(SiteRoutes.Game.resolve(
id: gameId,
throwaway: "play"
))
if (notPlayMode)
history.push(SiteRoutes.Game.resolve(
id: gameId,
throwaway: "kicked"
));
getUpdate();
, [started, iWasKicked, amInGame]);
React.useEffect(() =>
if(gameData?.game?.roundStarted)
if(isGameStarted == '0')
console.log("round is started");
setIsGameStarted('1');
, [gameData]);
const skipPlayer = (game_string_id: any, target_turn: any, chooserGuid: any) =>
return GameDataStore.skipPlayer(game_string_id, target_turn, chooserGuid);
const interval = () =>
let timer = setSeconds, minutes, seconds;
let chooserGuid = localStorage.getItem('chooserGuid');
let game_string_id = localStorage.getItem('game_id');
let target_turn = localStorage.getItem('target_turn');
let is_called = localStorage.getItem('is_called');
console.log("isGameStarted : "+isGameStarted);
if(isGameStarted == '0')
if (typeof timer !== undefined && timer != null)
minutes = parseInt(timer / 60 as any, 10);
seconds = parseInt(timer % 60 as any, 10);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
//console.log("test");
console.log(minutes + ":" + seconds);
setUpdateShowTimer(minutes+":"+seconds);
if (timer == 0)
skipPlayer(game_string_id, target_turn, chooserGuid);
if(intervalData != undefined && intervalData!== null)
clearInterval(intervalData);
if (--timer < 0)
if(intervalData != undefined && intervalData!== null)
clearInterval(intervalData);
setSeconds -= 1;
const startTimer = () =>
console.log("called again");
//interval_counter = setInterval(interval,1000);
setIntervalData(setInterval(interval,1000));
const getUpdate = () =>
if(gameData?.game?.players && gameData?.game?.id)
let game_id = gameData.game.id;
let all_players = gameData.game.players;
let all_player_id = Object.keys(all_players);
let filteredAry = all_player_id.filter(e => e !== userData.playerGuid);
console.log("user player guid:"+userData.playerGuid);
console.log("guid:"+chooserGuid);
console.log("all players:"+all_player_id);
console.log("new array:"+filteredAry);
let target_item = filteredAry.find((_, i, ar) => Math.random() < 1 / (ar.length - i));
if(typeof target_item !== undefined && target_item!=null)
localStorage.setItem('target_turn',target_item);
localStorage.setItem('is_started','0');
if(typeof game_id !== undefined && game_id!=null)
localStorage.setItem('game_id',game_id);
if(typeof chooserGuid !== undefined && chooserGuid!=null)
localStorage.setItem('chooserGuid',chooserGuid);
if(isChooser)
if(isCalled == '0')
setIsCalled("1");
startTimer();
else
//clearInterval(intervalData);
const isOwner = ownerGuid === userData.playerGuid;
const isChooser = playerGuid === chooserGuid;
const amSpectating = playerGuid in ...(spectators ?? ), ...(pendingPlayers ?? ) ;
const playerGuids = Object.keys(players ?? );
const roundsToWin = getTrueRoundsToWin(gameData.game as ClientGameItem);
const winnerGuid = playerGuids.find(pg => (players?.[pg].wins ?? 0) >= roundsToWin);
const inviteLink = (settings?.inviteLink?.length ?? 0) > 25
? `$settings?.inviteLink?.substr(0, 25)...`
: settings?.inviteLink;
const meKicked = kickedPlayers?.[playerGuid];
const tablet = useMediaQuery('(max-width:1200px)');
const canChat = (amInGame || amSpectating) && moment(dateCreated).isAfter(moment(new Date(1589260798170)));
const chatBarExpanded = chatData.sidebarOpen && !tablet && canChat;
/**********************************************/
/********************************************/
return (
<div style= maxWidth: chatBarExpanded ? "calc(100% - 320px)" : "100%" >
<div style= minHeight: "70vh" >
iWasKicked && (
<Alert variant="filled" severity="error">
<Typography>
meKicked?.kickedForTimeout ? "You were kicked for being idle. You may rejoin this game any time!" : "You left or were kicked from this game"
</Typography>
</Alert>
)
!winnerGuid && settings?.inviteLink && (
<Typography variant="caption">
Chat/Video Invite: <a href=settings.inviteLink target="_blank" rel="nofollow noreferrer">inviteLink</a>
</Typography>
)
winnerGuid && (
<ShowWinner />
)
!winnerGuid && (
<ErrorBoundary>
updateShowTimer isGameStarted
(!started || !(amInGame || amSpectating)) && (
<BeforeGame gameId=gameId isOwner=isOwner />
)
started && amInGame && !isChooser && (
[
<GamePlayWhite />
]
)
started && amInGame && isChooser && (
[
<GamePlayBlack />
]
)
started && amSpectating && (
<GamePlaySpectate />
)
</ErrorBoundary>
)
</div>
</div>
);
;
【问题讨论】:
interval
在哪里运行?你能告诉我们完整的组件代码吗?或者更好的是,创建一个重现问题的代码框?
useEffect 没有触发,因为依赖是一个对象 gameData,useEffect 不会在嵌套属性更改时调用。更多信息:***.com/questions/56010536/…
这其实是个好问题。我做了一个更简单的演示,显示useState
变量在setInterval
内部没有更新。 但是使用useRef
解决了这个问题。 codesandbox.io/s/youthful-wing-fdegd?file=/src/App.js
@MoshFeu 当 setTimer 运行时,您将一个函数传递给 setTimeout,该函数关闭变量,但 setTimer 仅在您单击按钮时运行,因此该函数仅在当时传递给 setTimeout 并关闭 @ 987654338@当时有。它正在关闭stale closure 的variable
。
这是一个很好的见解和参考@HMR,谢谢。
【参考方案1】:
如果您确定每次更新游戏数据时页面都会呈现,那么您可以这样做。
React.useEffect(() =>
if(gameData?.game?.roundStarted)
if(isGameStarted == '0')
console.log("round is started");
setIsGameStarted('1');
, [gameData?.game?.roundStarted]);
useEffect 不会遍历对象中的所有道具,因此您必须明确地将确切的值放入监视列表中。
【讨论】:
【参考方案2】:在你传递给 setInterval 的函数中有一堆stale closures 这是一个工作定时器的例子:
const App = () =>
const [
stateInInterval,
setStateInInterval,
] = React.useState(
count: 0,
running: false,
);
const interval = () =>
setStateInInterval((current) =>
if (!current.running)
clearInterval(current.interval);
return current;
if (current.count > 9)
return ...current, running: false ;
return
...current,
count: current.count + 1,
;
);
;
const startTimer = () =>
if (stateInInterval.running)
//already running
return;
setStateInInterval((current) => (
...current,
interval: setInterval(interval, 1000),
running: true,
));
;
const stopTimer = () =>
setStateInInterval((current) => (
...current,
running: false,
));
;
const resetTimer = () =>
setStateInInterval((current) => (
...current,
count: 0,
));
;
return (
<div>
<button onClick=startTimer>start timer</button>
<button onClick=stopTimer>stop timer</button>
<button onClick=resetTimer>reset timer</button>
<h4>stateInInterval.count</h4>
</div>
);
;
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
【讨论】:
非常感谢那个链接,这篇文章终于让我明白了为什么会这样。以上是关于反应useState不更新值的主要内容,如果未能解决你的问题,请参考以下文章
反应输入字段不使用 onClick 事件更新 useState
UseState 未使用 TextInput 正确更新 |反应