Reputation: 4279
On startup I use useEffect
to fetch some data from local storage. If the fetch was successful I want to send an action and change the state of the component. After dispatching the action, I see that the reducer received an action and returned the new state, but I don't see any change in the component when I try to log the new state. What can be the reason for this behaviour.
Here's the action.ts
:
import {TOKEN_VALIDITY} from './actionTypes'
export const setTokenValidity = (isTokenValid: boolean) => ({
type: TOKEN_VALIDITY,
isTokenValid
})
Here's the reducer.ts
:
const auth = (state = false, action: any) => {
// after dispatch I see this log in the console value is true
console.log('auth reducer action type is ' + action.type + ' value is ' + action.isTokenValid)
switch(action.type) {
case TOKEN_VALIDITY:
return action.isTokenValid
default:
return state
}
}
export default auth
In the component I want to update the state of isTokenValid, but I always get undefined for the value.
This is the component code
const Stack = createStackNavigator();
let userToken = null;
const App = (props:any) => {
useEffect(() => {
const bootstrapAsync = async () => {
try {
userToken = await retrieveData('Token',null);
SplashScreen.hide();
if(userToken != null) {
props.setTokenValidity(true)
}
// this logs undefined for props.isTokenValid -- why???
console.log("after token isValid: " + props.isTokenValid)
console.log('token ' + userToken);
} catch (e) {
// Restoring token failed
}
};
bootstrapAsync();
}, []);
return (
<NavigationContainer>
<Stack.Navigator >
{!props.isTokenValid ? (
<>
<Stack.Screen name="Login" component={Login} options={{ headerShown:false }}/>
<Stack.Screen name="Home" component={Home} options={{ headerShown:false }}/>
</>
) : (
<Stack.Screen name="Home" component={Home} options={{ headerShown:false }}/>
)}
</Stack.Navigator >
</NavigationContainer>
);
};
const mapDispatchToProps = (dispatch:any) => ({
setTokenValidity: (isTokenValid:boolean) => dispatch(setTokenValidity(isTokenValid))
})
const mapStateToProps = (state:any) => ({
isTokenValid: state.isTokenValid
})
export default connect(mapStateToProps, mapDispatchToProps)(App);
Upvotes: 0
Views: 950
Reputation: 135
The way I solved it in my project, is by creating multiple useEffects.
In order to solve your problem you need to do 2 things:
bootstrapAsync()
userToken
You have already did no.1 successfully, And your current problem is that your component does not updates when it receives new data, aka when userToken
updates.
The solution:
Write another useEffect function which will be rendered 2 times: one time on component load(which we will ignore because the fetch isn't done yet) and another time when userToken
value updates.
In order to avoid running our new useEffect on component load, we need to create a new state, which we will call allowNavigation.
allowNavigation
prop will be set to true only after the fetch is complete.
Then, only when allowNavigation is set to true
, we can check userToken
and handle it properly.
The following code will help you:
const App = (props:any) => {
const [allowNavigate, setAllowNavigate] = useState(false);
useEffect(() => {
const bootstrapAsync = async () => {
try {
userToken = await retrieveData('Token',null);
SplashScreen.hide();
setAllowNavigate(true);
...
};
bootstrapAsync();
}, []);
useEffect(() => {
if (allowNavigate)
// this will now log the correct values
console.log("after token isValid: " + props.isTokenValid)
console.log('token ' + userToken);
}, [allowNavigate]);
...
Upvotes: 0
Reputation: 4346
Reducers return a state object so you might wanna try doing this:
switch(action.type) {
case TOKEN_VALIDITY:
return { ...state, isTokenValid: action.isTokenValid }
default:
return state
}
Upvotes: 0
Reputation: 2609
In the first rendering, i.e. when the useEffect
is fired and you call the method using props.setTokenValidity
to set the token validity, the token gets set. However, the console also gets printed on the same rendering.
When the state gets updated and you get an updated one using the props.isTokenValid
, this is the 2nd re-rendering(not the same rendering when useEffect was called) and the useEffect
doesn't fire, therefore we don't see the console being printed with the new value.
If you for some reason want to log when isTokenValid
is set, use another useEffect
useEffect(() => {
console.log("after token isValid: " + props.isTokenValid)
},[props.isTokenValid]);
Upvotes: 1