Невозможно выполнить условный рендеринг с использованием навигации react в react native

#typescript #react-native #react-navigation #rtk-query

Вопрос:

магазин.ts

 import { configureStore, getDefaultMiddleware, CaseReducer} from '@reduxjs/toolkit';
// import { loginUser } from './slices/auth';
// import { createStore } from 'redux';
// import loginScreen from './slices/auth';
import { combineReducers } from 'redux';
import { authApi, useLoginUserMutation } from './api/authApi';
import authSlice, { loginUser } from './slices/updatedAuth';
import auth, { loginUser as loginUser2 } from './slices/auth';
import { loginRequest } from '../types/auth';

export type type_1 = {
    initial: boolean,
    test: string
}

let initialCustomState: type_1 = {
    initial: false,
    test: "abc"
};

const rootReducer = combineReducers({ [authApi.reducerPath]: authApi.reducer});

// const store = configureStore({
//   reducer: { rootReducer },
//   middleware: (getDefaultMiddleware) =>
//     getDefaultMiddleware().concat(authApi.middleware)
// });

const store = configureStore({
    reducer: {
        authSlice: authSlice
    },
    //  middleware: (getDefaultMiddleware) =>
    //  getDefaultMiddleware().concat(authApi.middleware),
})

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;

export default store;
// store.dispatch(authApi.endpoints.loginUser({ email: "abc", password: "avc" }));
 

authslice.ts

 import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { loginRequest } from '../../types/auth';
import { authApi } from '../api/authApi';
import { TimeZone, user } from '../../types/user';

type authState = {
    authToken: string,
    userKey: string,
    user: user | null,
}

let initialState: authState = {
    authToken: '',
    userKey: '',
    user: null,
}

export const authSlice = createSlice({
    name: 'authInfo',
    initialState,
    reducers: {
        loginUser: (state: authState, action: PayloadAction<loginRequest>) => {
            state.authToken = action.payload.email;
        },
        setUserDetails: (state: authState, action: PayloadAction<user>) => {
            state.authToken = action.payload.auth_token;
            state.userKey = action.payload.user_key;
            state.user = action.payload;
        },
        resetAuth: (state: authState, action: PayloadAction<void>) => {
            state.user = null;
            state.authToken = '';
        },
        setAuthToken : (state : authState, action : PayloadAction<string>) => {
            state.authToken = action.payload;
        }
    },
    extraReducers: (builder) => {
        builder.addMatcher(authApi.endpoints.loginUser.matchPending, (state, action) => {
            console.log('pending', action);
            console.log(state);
        })
            .addMatcher(authApi.endpoints.loginUser.matchFulfilled, (state, action) => {
                console.log('fulfilled', action);
                state.authToken = action.payload.auth_token;
                state.user = action.payload;
                state.userKey = action.payload.user_key;
                console.log("updated state" , state);

            })
            .addMatcher(authApi.endpoints.loginUser.matchRejected, (state, action) => {
                console.log('rejected', action);
            });
    },
});


export default authSlice.reducer;

export const { loginUser, setUserDetails, resetAuth, setAuthToken } = authSlice.actions;

 

StackNavigator.tsx

 
const Stack = createNativeStackNavigator();


const getData = async () => {
    try {
      const jsonValue = await AsyncStorage.getItem('logged_in_user')
      console.log(jsonValue);
      return jsonValue != null ? JSON.parse(jsonValue) : null;
        
    } catch(e) {
      // error reading value
    }
  }




export default function StackNavigator() {
    let state = store.getState();
    const dispatch = useAppDispatch();
    let [isLoading, setIsLoading] = useState(true);
    let [authToken, setAuthToken] = useState(state.authSlice.authToken);
    let [isSignedIn, setIsSignedIn] = useState(false);
    useEffect( () => {
      getData().then(res => {
        console.log(res);
        if(res !== null) {
        setIsLoading(false);
        setAuthToken(res.auth_token);
        dispatch(authSlice.actions.setAuthToken(res.auth_token));
        setIsSignedIn(true);
  
        } else {
            setIsLoading(false);
            setAuthToken('');
            setIsSignedIn(false);
        }
    }).catch(err => {
        console.log(err);
        setIsLoading(false);
        setAuthToken('');
        setIsSignedIn(false);
    })
    });

    if(isLoading) {
        return <FirstScreen></FirstScreen>
    } else {
    return (
      <Stack.Navigator>
      <>
      {
        store.getState().authSlice.authToken !== '' ?
             <Stack.Screen name = "Drawer" component={DrawerNavigator} options = {{headerShown : false}}></Stack.Screen> :
             <>  
           <Stack.Screen name = "Login" component = {LoginScreen} options={{headerShown : false}} ></Stack.Screen>
         <Stack.Screen name = "ForgotPassword" component = {ForgotPasswordScreen} options = {{headerShown : false}}></Stack.Screen>
        </>
      }
         
      </>
      </Stack.Navigator>
    );
    }
}
 

App.tsx

 import 'react-native-gesture-handler';
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import {View} from 'react-native'
import { createAppContainer, StackRouter } from 'react-navigation'
import { createDrawerNavigator, DrawerItem, DrawerScreenProps } from '@react-navigation/drawer';
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { Feather } from '@expo/vector-icons';
import { FontAwesome5 } from '@expo/vector-icons';
import useCachedResources from './hooks/useCachedResources';
import useColorScheme from './hooks/useColorScheme';
import Navigation from './navigation';
import DrawerItems from './constants/DrawerItems';
import store from './src/redux/store';
import { Provider } from 'react-redux';
import AccountScreen from './screens/AccountScreen';
import AlarmsScreen from './screens/AlarmsScreen';
import TasksScreen from './screens/TasksScreen';
import SupportScreen from './screens/SupportScreen';
import LoginScreen from './screens/LoginScreen';
import Header  from './screens/Header';
import SafeAreaProvider  from 'react-native-safe-area-context';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import ForgotPasswordScreen from './screens/ForgotPasswordScreen';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { authApi, useLoginUserMutation } from './src/redux/api/authApi';
import DrawerNavigator from './src/navigators/drawerNavigator';
import { authSlice } from './src/redux/slices/updatedAuth';
import StackNavigator  from './src/navigators/stackNavigator';




const Drawer = createDrawerNavigator();
const Stack = createNativeStackNavigator();


export default function App() {
  const state = store.getState();

  
  return (
 
       <NavigationContainer>
            <Provider store={store}>
      <StackNavigator></StackNavigator>
      </Provider>
  </NavigationContainer>
  
   
  );
}

 

Экран входа в систему.tsx

 
import React, {useState} from 'react'
import { StyledButton, StyledTextInput, Logo, Header, Background, setDataInAsyncStorage} from '../src/utils/utils'
import {Text, View, TouchableOpacity} from 'react-native'
import { emailValidator } from './../src/validators/emailValidator';
import { passwordValidator } from './../src/validators/passwordValidator';
import { styles } from './../src/styles/styles';
import { Navigation } from '../src/types/genericTypes';
import { useLoginUserMutation } from '../src/redux/api/authApi';
import {Snackbar} from 'react-native-paper'
import AsyncStorage from '@react-native-async-storage/async-storage';
import { authSlice, setAuthToken } from './../src/redux/slices/updatedAuth';
import { useAppDispatch } from './../src/redux/hooks';
import { useDispatch } from 'react-redux';
import { getErrorMessageFromErrorListDTOResponse } from '../src/utils/utils';

type Props = {
  navigation : Navigation;
}

const LoginScreen = ({navigation} : Props) => {
  const [email, setEmail] = useState({value : '', error : ''});
  const [password, setPassword] = useState({value : '', error : ''});
  const [loginUser, {isLoading, isError}] = useLoginUserMutation();
  const [error, setError] = useState('');
  const [visible, setVisible] = useState(false);
  const dispatch = useAppDispatch();

  const onDismissSnackBar = () => {
    setVisible(false);
  }
  
  const _onLoginPressed = () => {
     const emailError = emailValidator(email.value);
     const passwordError = passwordValidator(password.value);
    
    

    if (emailError || passwordError) {
      setEmail({ ...email, error: emailError });
      setPassword({ ...password, error: passwordError });
      return;
    }
    var loginUserRequest = {
      email : email.value,
      password : password.value,
    };
    loginUser(loginUserRequest).unwrap().then((payload) =>  {
    setDataInAsyncStorage('logged_in_user', JSON.stringify(payload));
    dispatch(authSlice.actions.setAuthToken(payload.auth_token));
   }).catch((error) => {
    console.log(error); 
    setError(getErrorMessageFromErrorListDTOResponse(error)); setVisible(true)});

    // navigation.navigate('Dashboard');
  };
  return (
    <Background>
        <Logo />

      <Header>Sign In</Header>
         <View>
      <StyledTextInput      
        label="Email"
        returnKeyType="next"
        value={email.value}
        onChangeText={text => setEmail({ value: text, error: '' })}
        error={!!email.error}
        errorText={email.error}
        autoCapitalize="none"
        autoCompleteType="email"
        textContentType="emailAddress"
        keyboardType="email-address"></StyledTextInput>
      </View>
      <View>
          <StyledTextInput       label="Password"
        returnKeyType="done"
        value={password.value}
        onChangeText={text => setPassword({ value: text, error: '' })}
        error={!!password.error}
        errorText={password.error}
        secureTextEntry>

        </StyledTextInput>
        <Snackbar
        visible={visible}
        onDismiss={onDismissSnackBar}>
        {error}
      </Snackbar>
 
      </View>
      <View style={styles.forgotPassword}>
        <TouchableOpacity
        onPress={() => {
          navigation.navigate("ForgotPassword")
        }}
        >
          <Text style={styles.forgotPasswordLabel}>Forgot your password?</Text>
        </TouchableOpacity>
      </View>
      <StyledButton mode="outlined" onPress={_onLoginPressed} color = "#BAC8EF">
        Login
      </StyledButton>

      </Background>
  )
}

export default LoginScreen
 

Я относительно новичок в использовании redux, react native, rtk-запросов и централизованного управления состоянием.
Проблема, с которой я сталкиваюсь, заключается в том, что все состояния обновляются должным образом, но приложение не перенаправляется для отображения надлежащего текущего состояния, которое является маркером аутентификации, несмотря на то, что обновление не перенаправляет на новый навигатор экрана ящика условно. Однако он не выполняет никаких действий, но отлично работает при обновлении

Ответ №1:

Вы не должны делать что-то вроде

     let state = store.getState();
    let [authToken, setAuthToken] = useState(state.authSlice.authToken);
 

когда-либо.

Здесь есть две вещи:

  • useState предназначен для локального состояния реакции компонента и не имеет ничего общего с Redux. Даже если значение состояния изменится, так как аргумент to useState отмечает только начальное состояние, authToken теперь оно больше никогда не изменится, если вы не позвоните setAuthToken .
  • вы никогда не должны использовать store.getState() внутри компонента.

Вместо этого сделайте

 const authToken = useSelector(state => state.authSlice.authToken)