在 redux 状态更改后,React 导航不会更改导航屏幕
Posted
技术标签:
【中文标题】在 redux 状态更改后,React 导航不会更改导航屏幕【英文标题】:React navigation doesn't change navigation screen after redux state changes 【发布时间】:2021-07-21 00:16:59 【问题描述】:即使在 redux 状态更改后,导航也不会更改为 MainScreen。我已经验证 authState 从 "isAuthenticated": false, "isLoading": true, "token": null 更改为 "isAuthenticated": true, "isLoading": false, "token": "some_token",但是导航页面停留在登录页面(在 LandingNavigator 内),而不是转到 MainNavigator。
AppNavigation.js
const Navigator = () =>
var [authStat, setAuthStat] = useState()
useEffect(()=>
var authState = store.getState().auth
setAuthStat(authState)
, [authStat])
console.log(authStat);
if(authStat.isLoading)
return(
<Stack.Navigator>
<Stack.Screen
options=headerShown: false
name="Splash"
component=ShowSplash
/>
</Stack.Navigator>
)
else
return(
<Stack.Navigator>
authStat.isAuthenticated ?
(<Stack.Screen
options=headerShown: false
name="Main"
component=MainNavigator
/>)
:
(<Stack.Screen options=headerShown: false name="Landing" component=LandingNavigator />)
</Stack.Navigator>
)
;
AuthAction.js
export function loginRequest()
return
type: "LOGIN_REQUEST",
;
export function loginSuccess(data)
return
type: "LOGIN_SUCCESS",
payload: data
;
export function loginFailure(data)
return
type: "LOGIN_FAILURE",
payload: data
;
export function restoreToken(data)
return
type: "RESTORE_TOKEN",
payload: data
;
export function logOut()
return
type: "LOGOUT",
;
AuthReducer.js
/* eslint-disable comma-dangle */
const authState =
isLoading: true,
isAuthenticated: false,
token: null
;
export const authReducer = (state = authState, action) =>
const newState = JSON.parse(JSON.stringify(state));
switch (action.type)
case 'LOGIN_REQUEST':
return
isLoading: true, // Show a loading indicator.
isAuthenticated: false
case 'RESTORE_TOKEN':
return
isLoading: false, // Show a loading indicator.
isAuthenticated: true,
token: action.payload
case 'LOGIN_FAILURE':
return
isLoading: false,
isAuthenticated: false,
error: action.error
case 'LOGIN_SUCCESS':
return
isLoading: false,
isAuthenticated: true, // Dismiss the login view.
token: action.payload
case 'LOGOUT':
return
isLoading: false, // Show a loading indicator.
isAuthenticated: false,
token: null
default:
return newState;
return newState;
;
Auth.js
import AsyncStorage from '@react-native-async-storage/async-storage';
import useDispatch from 'react-redux';
import loginRequest, loginSuccess, loginFailure, logOut from '../redux/actions/authAction';
export const storeToken = async (value) =>
try
await AsyncStorage.setItem('token', value)
catch (e)
// saving error
export const getToken = async () =>
try
const value = await AsyncStorage.getItem('token')
if(value !== null)
return value
else
return null
catch(e)
// error reading value
console.log(e);
export const removeToken = async () =>
try
await AsyncStorage.removeItem('token')
catch(e)
// remove error
console.log('token removed.')
export const isLoggedIn = async () =>
if(await getToken() != null)
return true
return false
export const signOut = () =>
removeToken()
export default storeToken, getToken, removeToken, isLoggedIn, signOut
LoginScreen.js
/* eslint-disable comma-dangle */
import React, useEffect, useState, useCallback from 'react';
import
View,
TouchableHighlight,
Text,
TextInput,
TouchableWithoutFeedback,
Keyboard,
ScrollView
from 'react-native';
import login from '../../api/apiQueries'
import storeToken from '../../auth/auth'
import store from '../../redux/store';
import styles from './styles';
import useDispatch, useSelector from 'react-redux';
const authState = store.getState().auth;
const LogInScreen = (route, navigation) =>
const [userName, setUserName] = useState("")
const [password, setPassword] = useState("")
const dispatch = useDispatch()
onPressLogButton = () =>
dispatch(login(userName, password))
return (
<TouchableWithoutFeedback onPress=() => Keyboard.dismiss()>
<ScrollView style=styles.container>
<View>
<Text style=styles.title>Sign in</Text>
<View style=styles.inputContainer>
<TextInput
style=styles.input
placeholder="User Name"
onChangeText=text => setUserName(text)
value=userName
/>
</View>
<View style=styles.inputContainer>
<TextInput
style=styles.input
placeholder="Password"
onChangeText=text => setPassword(text)
value=password
/>
</View>
<View style=styles.logContainer>
<TouchableHighlight
style=styles.loginContainer
onPress=() => onPressLogButton()
>
<Text style=styles.logTxt>Log in</Text>
</TouchableHighlight>
/* <Text style=styles.orTxt>OR</Text> */
/* <TouchableHighlight
style=styles.facebookContainer
onPress=() => this.onPressFacebookButton()
>
<Text style=styles.facebookTxt>Facebook Login</Text>
</TouchableHighlight> */
</View>
</View>
</ScrollView>
</TouchableWithoutFeedback>
);
export default LogInScreen
【问题讨论】:
你正在使用钩子,你正在使用 react-redux;您为什么不使用useSelector
而不是直接访问商店并将商店详细信息手动复制到本地状态?这样,一旦 redux 状态发生变化,您就不会得到任何更新。
黄金!我刚刚用 redux store 替换了本地状态,并使用了你提到的 use 选择器,它起作用了。想知道使用 getStore 和 useSelector 获得商店有什么区别?
请检查我的回答;如果您接受,我将不胜感激。
【参考方案1】:
正如 cmets 中所讨论的,解决方案是使用 useSelector
挂钩,或者使用 connect
方法的 mapStateToProps
参数订阅您的组件以存储更新。这样,只要商店通过分派的操作更新,它就会运行。
来自文档:
useSelector()
也会订阅 Redux 存储,并运行你的 每当调度一个动作时选择器。 Link
这意味着,例如,对于您的 AppNavigation.js,您可以将代码更改为:
import useSelector from 'react-redux';
const Navigator = () =>
const authStat = useSelector((state) => state.auth);
if(authStat.isLoading)
return(
...
通过直接访问从存储中读取就可以做到这一点,但这并不意味着订阅未来的更改。
【讨论】:
以上是关于在 redux 状态更改后,React 导航不会更改导航屏幕的主要内容,如果未能解决你的问题,请参考以下文章
React s-s-r路由器-导航到子路由时的redux状态丢失