admin管理员组文章数量:1355658
I'm using hooks for updating state. in my code I have an AppState event listener and whenever it triggers I'm updating the appState using setAppState, however the appState inside the event listener did not change. but the value is updating outside the listener. can anyone explain why is behaving like that?
Here is my code:
import React, { FunctionComponent, useEffect, useState } from "react"
import { View, AppState, AppStateStatus } from "react-native"
const Test: FunctionComponent<any> = (props: any) => {
const [appState, setAppState] = useState < AppStateStatus > (AppState.currentState)
useEffect(() => {
AppState.addEventListener("change", _handleAppStateChange)
},[])
const _handleAppStateChange = (nextAppState: AppStateStatus) => {
//appState not Changing here
console.log(appState, "app state")
if (appState.match(/inactive|background/) && nextAppState === "active") {
console.log("App has e to the foreground!")
activateRealtime()
} else if (appState === "active" && nextAppState.match(/inactive|background/)) {
console.log("App has e to background!")
}
setAppState(nextAppState)
}
//appState updated here
console.log(appState, "app state")
return <View />
}
I'm using hooks for updating state. in my code I have an AppState event listener and whenever it triggers I'm updating the appState using setAppState, however the appState inside the event listener did not change. but the value is updating outside the listener. can anyone explain why is behaving like that?
Here is my code:
import React, { FunctionComponent, useEffect, useState } from "react"
import { View, AppState, AppStateStatus } from "react-native"
const Test: FunctionComponent<any> = (props: any) => {
const [appState, setAppState] = useState < AppStateStatus > (AppState.currentState)
useEffect(() => {
AppState.addEventListener("change", _handleAppStateChange)
},[])
const _handleAppStateChange = (nextAppState: AppStateStatus) => {
//appState not Changing here
console.log(appState, "app state")
if (appState.match(/inactive|background/) && nextAppState === "active") {
console.log("App has e to the foreground!")
activateRealtime()
} else if (appState === "active" && nextAppState.match(/inactive|background/)) {
console.log("App has e to background!")
}
setAppState(nextAppState)
}
//appState updated here
console.log(appState, "app state")
return <View />
}
Share
Improve this question
edited Jul 7, 2020 at 10:07
Mervzs
asked Jul 7, 2020 at 10:03
MervzsMervzs
1,1548 silver badges21 bronze badges
14
-
You should fix this
const [appState, setAppState] = useState < AppStateStatus > AppState.currentState
– Joshua Commented Jul 7, 2020 at 10:07 -
How do you know the state isn't updating inside the callback?
setAppState(nextAppState)
is the last call within the function. – Drew Reese Commented Jul 7, 2020 at 10:08 - its not the issue. changed it. i just forgot to include parantheses there. – Mervzs Commented Jul 7, 2020 at 10:08
- 2 @DrewReese. It updates. but inside the listener the value is still same. – Mervzs Commented Jul 7, 2020 at 10:32
-
1
@JaredSmith
State updates are asynchronous.
even though that is true, saying that will cause people to tryawait setState
. Also the main problem with setting state created with useState in functional ponents is stale closures – HMR Commented Jul 7, 2020 at 10:42
2 Answers
Reset to default 7In your code appState is a stale closure the linter should have told you that you have missing dependencies.
I think the following will work
const _handleAppStateChange = useCallback(
(nextAppState) =>
//use callback for state setter so you don't
// create needless dependency or (as you did)
// create a stale closure
setAppState((currentAppState) => {
//logs current appstate
console.log(currentAppState, 'app state');
if (
currentAppState.match(/inactive|background/) &&
nextAppState === 'active'
) {
console.log('App has e to the foreground!');
activateRealtime();
} else if (
currentAppState === 'active' &&
nextAppState.match(/inactive|background/)
) {
console.log('App has e to background!');
}
return nextAppState;
}),
//only pass function as _handleAppStateChange
// on mount by providing empty dependency
[]
);
useEffect(() => {
AppState.addEventListener(
'change',
_handleAppStateChange
);
//clean up code when ponent unmounts
return () =>
AppState.removeEventListener(
'change',
_handleAppStateChange
);
//_handleAppStateChange is a dependency but useCallback
// has empty dependency so it is only created on mount
}, [_handleAppStateChange]);
Acceptable Solution
I was doing what the accepted answer suggested but it was still not working for me. I not only needed to call useCallback
but I also needed to add my element's ref to the useEffect
's dependency array as it was changing at some point and so the triggered callback was still using old data. I'm not sure how the callback was even being triggered then if element.current
was replaced...
const elementRef = useRef(null);
useEffect(() => {
elementRef.current.addEventListener("change", _handleAppStateChange)
return () => elementRef.current.removeEventListener("change", _handleAppStateChange)
}, [elementRef.current])
...
return (
<div ref={elementRef}>
...
Preferred Solution
However, as in my case, if you are rendering the element yourself, moving the event directly to the target element also fixes the issue. It not only makes the code simpler but it also just works whether you use useCallback
or not!
const elementRef = useRef(null);
...
return (
<div ref={elementRef} onChange={_handleAppStateChange}>
...
In this case, React will directly reattach the updated function to the element on every re-render.
本文标签:
版权声明:本文标题:javascript - why updated state not reflected inside an event listener: React Native, Hooks - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744015580a2576264.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论