#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. Даже если значение состояния изменится, так как аргумент touseState
отмечает только начальное состояние,authToken
теперь оно больше никогда не изменится, если вы не позвонитеsetAuthToken
.- вы никогда не должны использовать
store.getState()
внутри компонента.
Вместо этого сделайте
const authToken = useSelector(state => state.authSlice.authToken)