dispatch Action后组件状态未更新,props值仍为undefined的原因排查
我在启动时用useEffect从本地存储获取数据,成功后dispatch action更新组件状态。已经确认reducer收到action并返回了新状态,但打印组件的props.isTokenValid始终是undefined,相关代码如下:
action.ts
import {TOKEN_VALIDITY} from './actionTypes' export const setTokenValidity = (isTokenValid: boolean) => ({ type: TOKEN_VALIDITY, isTokenValid })
reducer.ts
const auth = (state = false, action: any) => { // dispatch后能看到这个日志,值是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
组件代码
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) } // 这里打印props.isTokenValid是undefined,为什么? console.log("after token isValid: " + props.isTokenValid) console.log('token ' + userToken); } catch (e) { // 恢复token失败 } }; 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);
这事儿我太熟了,核心问题就是Redux的状态更新是异步的,你踩了「同步代码里想拿异步更新后的值」的坑,具体拆解下:
1. 为什么打印出来是undefined?
当你调用props.setTokenValidity(true)之后,Redux内部要走这几步:
- 把action传给reducer计算新状态
- 更新store里的状态
- 通知所有连接的组件重新计算props并触发重渲染
这整个流程不是同步完成的,所以你在dispatch之后立刻打印props.isTokenValid,拿到的还是组件当前的旧props值(这里初始值如果store配置没问题的话应该是false,但你说undefined,可能还有store映射的问题,后面说)。
简单说就是:dispatch发出更新指令,但新状态还没传导到组件,你就急着打印了。
2. 怎么验证新状态确实生效了?
你可以加一个监听props.isTokenValid变化的useEffect,这样状态更新后就能看到最新值:
useEffect(() => { console.log("当前isTokenValid的最新值:", props.isTokenValid); }, [props.isTokenValid]); // 依赖数组里放props.isTokenValid,值变就触发
或者直接在组件的顶层(useEffect外面)打印props.isTokenValid,组件重渲染时就会打印最新值。
3. 额外排查点:你提到的undefined可能还有另一个原因
你的reducer初始状态是false,但props.isTokenValid一开始是undefined,这可能是mapStateToProps的映射有问题。检查你的store配置,是不是用combineReducers的时候,把auth reducer对应到了isTokenValid这个key?比如:
// store/index.ts 之类的文件里 import { combineReducers, createStore } from 'redux'; import auth from './reducer'; const rootReducer = combineReducers({ isTokenValid: auth, // 这里必须和mapStateToProps里的state.isTokenValid对应 }); export const store = createStore(rootReducer);
如果combineReducers时auth对应的key不是isTokenValid,那state.isTokenValid自然就是undefined,这时候即使reducer返回了true,组件也拿不到。
4. 关于导航逻辑的说明
你不用担心导航判断会有问题,因为当Redux状态更新后,组件会自动重渲染,这时候会用新的props.isTokenValid来决定显示哪个页面,只是你同步打印的时机不对而已。
内容的提问来源于stack exchange,提问作者Keselme

