使用 React 本机导航调度 redux 时注销错误

Posted

技术标签:

【中文标题】使用 React 本机导航调度 redux 时注销错误【英文标题】:Logout error on dispatch redux with React native navigation 【发布时间】:2021-07-06 13:20:59 【问题描述】:

在我的 React Native 应用程序中,实际上我需要使用导航和 redux 实现一个简单的注销,但是当我从我的主页 (按下注销按钮)发送注销操作时出现错误,因为在 Home 中,我呈现当前用户,并且在那一刻不存在。

我需要调度 Logout(删除当前用户)并导航到登录页面而不再次渲染 Home 或知道如何在渲染之前实现 componentWillMount.... 如何?

我的简历代码: (登录有效,我没有输入登录代码)

App.tsx

import React, useState from 'react';
import  NavigationContainer, StackActions  from '@react-navigation/native';
//... many imports

const store = createStore(rootReducer);

export default class App extends React.Component 
   constructor(props) 
    super(props);
    this.state =  loading: true ;
   

async componentDidMount() 
  //... some code


render()
   if (this.state.loading) 
     return (
       <Root>
         <AppLoading />
       </Root>
    );
   
   else 
     return (
       <Provider store=store>
        <Root>
          <NavigationContainer>
            <Navigator/>
          </NavigationContainer>
        </Root>
       </Provider>          
     );
   
  

Navigator.tsx

import React from 'react'
import  createStackNavigator  from '@react-navigation/stack'
import connect from 'react-redux'
import Login from '../Views/Login'
import Home from '../Views/Home'

const Stack = createStackNavigator();

const Navigator= (props) =>
   const users = props;
   return (
        <Stack.Navigator>
            console.log('Navigator here, '+users.current)
            users.current ===null ? 
                (<>    
                    <Stack.Screen name="Login" component=Login  options=headerShown:false/>
                </>)
            :   (<>    
                    <Stack.Screen name="Home" component=Home options=headerShown:false/>
                </>)
            
        </Stack.Navigator>
    );
 

const mapStateToProps=(state)=>
   const users = state;
   return users;


export default connect(mapStateToProps)(Navigator);

Home.tsx(在注销后导航?)

import React from 'react'
import Text, Container, Content, View, Button, Icon from 'native-base'
import Styles from './Styles/HomeStyles'
import connect from 'react-redux'
import Auth from '../Services/Auth/AuthService'

const Home =(props) => 
   const navigation, users = props;
   return(
    <Container >
        <Content >      
            <View style=Styles.content>                  
                <Text>Hi users.current.id</Text>
                <Button onPress=()=>Logout(users.current.id) >
                    <Icon name='exit' />
                </Button>
            </View>
        </Content>
    </Container>
   );

   async function Logout(id:string)
      console.log('Function start');
      await Auth.LogoutUser(id, props);
      navigation.navigate('Login');
   
 

const mapStateToProps=(state)=>
  const users = state;
  return users;


export default connect(mapStateToProps)(Home);

UserReducer.tsx

const Initial_State=
   current:null


export function UserReducer(state=Initial_State, action:any)
   switch (action.type)
    case 'ADD_USER':
        return
            ...state,
            current: action.user
        ;
    case 'DELETE_USER':
        return
            ...state,
            current: null
        ;
    default:
        return state;
   

AuthService.tsx(这里是调度)

export const Auth=
   LoginUser,
   LogoutUser


async function LogoutUser(id: string, props: any)
  await props.dispatch(type : 'DELETE_USER', id);


async function LoginUser(AuthData:IAuthData, props: any)
  //...some code

【问题讨论】:

【参考方案1】:

您的Home 组件期望从状态中选择一个用户对象。请注意,您应该只选择您想要的特定对象state.users.current,而不是选择state.users,然后查看users.current

除非我们有用户,否则我们不希望 Home 呈现,因此我们可以 useEffect 有条件地离开页面。这意味着我们实际上不需要await 注销,因为只要没有用户,导航就会自动发生。动作被调度 --> redux 状态改变 --> currentUser 属性改变 --> 效果运行 --> 导航发生。

const Home = (props) => 
  const  navigation, currentUser  = props;

  function Logout(id) 
    // no async/await here
    // navigation is triggered when currentUser updates
    Auth.LogoutUser(id, props);
  

  useEffect(() => 
    if (!currentUser) 
      navigation.navigate("Login");
    
  , [currentUser, navigation]);

  if (!currentUser) 
    // should only end up here momentarily while navigation is taking place
    return null;
  
  
  return (
    <Container>
      <Content>
        <View style=Styles.content>
          <Text>Hi currentUser.id</Text>
          <Button onPress=() => Logout(currentUser.id)>
            <Icon name="exit" />
          </Button>
        </View>
      </Content>
    </Container>
  );
;

const mapStateToProps = (state) => 
  return 
    currentUser: state.users.current
  ;
;

export default connect(mapStateToProps)(Home);

【讨论】:

- 不起作用,错误:TypeError: null is not an object (evalating 'currentUser.id') 此错误位于:in Home (created by ConnectFunction) 第一次只在 Home 中调用 useEffect,当我按下注销时它不会被调用(我知道是因为我在函数中放了一个 console.log()) 第一个错误没有意义,因为它说currentUsernull,但在我们尝试访问currentUser.id 之前,我们先做了if (!currentUser) return null; 是的,你是对的,我忘了那个条件。现在它起作用了。谢谢:)【参考方案2】:

在您的情况下,您现在可以通过更改用户变量来处理 null 状态

  <Text>Hi users.current.id</Text>

<Text>Hi users?.current?.id</Text>

但在实际应用中,您应该必须重置完整的应用状态,并且必须在该应用状态下管理您的条件。

例子:

import  combineReducers  from 'redux';
import UsersReducer from './UsersReducer';

  const appReducer = combineReducers(
      users: UsersReducer,
     // other Reducers if there is any.
    );

 const rootReducer = (state, action) => 
   // When logout action is dispatched  reset the action.
   if (action.type === 'USER_LOG_OUT') 
     state = undefined; // or to initial state if  you want any data without session 
    
    return appReducer(state, action);
   ;

export default rootReducer;

【讨论】:

state = undefined 这不是正确的 redux。 是的@Linda,在某些情况下不是,这就是为什么我提到我们可以根据我们的用例将其重置为某些初始状态或应用程序状态。 你需要return状态的新值。你不能这样做state = 在这种情况下,我们不会改变状态,我们只是为 state(local) 变量分配一些值,并将相同的变量传递给 app reducer。所以 appReducers 会返回一个新的状态。所以没有违反redux原则(状态突变)。

以上是关于使用 React 本机导航调度 redux 时注销错误的主要内容,如果未能解决你的问题,请参考以下文章

如何在异步函数中调用 redux 调度作为函数体?

来自根组件的 React/Redux 调度操作?

React Redux:调度时的无限循环

Redux & React Router:结合调度和导航(history.push)

Redux&React路由器:梳理调度和导航(history.push)

React Native Auth 与 React Navigation 和 Redux