【问题标题】:React navigation doesn't change navigation screen after redux state changes在 redux 状态更改后,React 导航不会更改导航屏幕
【发布时间】: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 获得商店有什么区别?
  • 请检查我的回答;如果您接受,我将不胜感激。

标签: reactjs react-native react-redux


【解决方案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(
...

通过直接访问从存储中读取就可以做到这一点,但这并不意味着订阅未来的更改。

【讨论】:

    猜你喜欢
    • 2021-04-21
    • 2020-08-15
    • 2022-01-20
    • 2020-11-28
    • 2018-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-01
    相关资源
    最近更新 更多