分派动作后组件状态不变

时间:2020-07-19 12:24:12

标签: reactjs react-native redux react-redux

在启动时,我使用useEffect从本地存储中获取一些数据。如果获取成功,我想发送一个动作并更改组件的状态。分派动作后,我看到减速器收到了一个动作并返回了新状态,但是当我尝试记录新状态时,我没有看到组件的任何更改。出现这种行为的原因可能是什么。

这里是action.ts

import {TOKEN_VALIDITY} from './actionTypes'

export const setTokenValidity = (isTokenValid: boolean) => ({ 
    type: TOKEN_VALIDITY,
    isTokenValid
})

这里是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

在组件中,我想更新isTokenValid的状态,但是我总是无法确定其值。

这是组件代码

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);

3 个答案:

答案 0 :(得分:1)

在第一个渲染中,即触发useEffect并使用props.setTokenValidity调用该方法来设置令牌有效性时,令牌就被设置了。但是,控制台也会在相同的渲染图上打印。

当状态得到更新并且您使用props.isTokenValid获得更新后的状态时,这是第二次重新渲染(调用useEffect时是不同的渲染),并且useEffect不会触发,因此我们看不到控制台打印有新值。

如果出于某种原因要在设置isTokenValid时登录,请使用另一个useEffect

useEffect(() => {
        console.log("after token isValid: " + props.isTokenValid) 

},[props.isTokenValid]);

答案 1 :(得分:0)

Reducer返回一个状态对象,因此您可能想尝试执行以下操作:

      switch(action.type) {
    
            case TOKEN_VALIDITY:
                return { ...state, isTokenValid: action.isTokenValid }
    
            default:
                return state
       }

答案 2 :(得分:0)

我在项目中解决该问题的方法是创建多个useEffects。

要解决您的问题,您需要做两件事:

  1. 您需要运行bootstrapAsync()
  2. 您需要检查 bootstrapAsync完成后以查看设置在userToken中的数据

您已经成功地完成了1号操作,并且当前的问题是您的组件在接收到新数据(也就是userToken更新时)没有更新。

解决方案:

编写另一个useEffect函数,该函数将呈现2次:一次在组件加载时(由于尚未完成获取,我们将忽略此操作),而另一次在userToken值更新时进行。

为了避免在组件加载时运行新的useEffect,我们需要创建一个新状态,我们将其称为allowNavigation。

allowNavigation属性仅在获取完成后才会设置为true。

然后,仅当allowNavigation设置为true时,我们才能检查userToken并正确处理。

以下代码将为您提供帮助:

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]);
...