React Native Navigation v6 身份验证流程
React native navigation v6 authentication flow
我有一个应用程序,我可以在其中 login/register 并且可以 signout/logout,我正在使用 react navigation v6
进行相同的操作,我可以登录,但是当我注销时没有立即注销我,我将不得不 hot restart
该应用程序,然后我已经注销了,它只是在没有热重启的情况下不注销我,同样,在注销时,我得到一个 error/warning:
error
:
The action 'NAVIGATE' with payload {"name":"Login"} was not handled by any navigator.
Do you have a screen named 'Login'?
If you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.
This is a development-only warning and won't be shown in production.
下面是我使用的代码:
上下文文件:
const initialState = {
user: null,
loading: false,
};
const UserContext = createContext(initialState);
export const UserProvider = ({children}) => {
const [globalState, dispatch] = useReducer(UserReducer, initialState);
return (
<UserContext.Provider value={{
user: globalState.user,
loading: globalState.loading,
dispatch,
}} >
{children}
</UserContext.Provider>
);
};
export default function() {
return useContext(UserContext);
}
导航画面:
const Navigation = () => {
const {user, dispatch, loading} = useAuth();
let navigationRef;
useEffect(() => {
setTimeout(() => {
navigationRef = createNavigationContainerRef(); // To avoid getting error: navigator is undefined while the component is mounting...
}, 500);
checkUserLoggedInStatus(dispatch, navigationRef);
}, [dispatch]);
return loading ? (
<LoadingScreen />
) : (
<Stack.Navigator screenOptions={{headerShown: false}}>
{!user ? (
<Stack.Group>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
</Stack.Group>
) : (
<Stack.Group>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Group>
)}
</Stack.Navigator>
);
};
checkUserLoggedInStatus
、login
& logout
效用函数:
export const login = (otp, userId, dispatch) => {
dispatch(SET_LOADING(true));
fetch(`${API_URL}/auth/login/`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({...}),
})
.then(response => response.json())
.then(data => {
if (data.error) {
console.log('[ERROR] While trying to login: ', data.error);
dispatch(SET_LOADING(false));
}
if (data.type === 'success') {
AsyncStorage.setItem('token', data.data.token)
.then(response => console.log(response))
.catch(err => console.log(err));
const userDetails = data.data.user;
dispatch(SET_USER_DETAILS(userDetails));
dispatch(SET_LOADING(false));
}
})
.catch(error => {
console.log('[ERROR] While logging in: ' + error.message);
dispatch(SET_LOADING(false));
});
};
export const checkUserLoggedInStatus = (dispatch, navigator) => {
dispatch(SET_LOADING(true));
AsyncStorage.getItem('token')
.then(token => {
if (token) {
fetch(`${API_URL}/user/`, {
method: 'GET',
headers: {...},
})
.then(response => response.json())
.then(data => {
if (data.type === 'success') {
const details = data.data.user;
dispatch(SET_USER_DETAILS(details));
dispatch(SET_LOADING(false));
}
if (data.error) {
console.log('[INFO] Your token expired.');
if (navigator.isReady()) {
navigator.navigate('Login');
}
}
})
.catch(error => {
console.log('[ERROR] While fetching profile: ' + error.message);
dispatch(SET_LOADING(false));
});
} else {
dispatch(SET_LOADING(false));
}
})
.catch(error => {
console.log('[ERROR] While fetching token: ' + error.message);
dispatch(SET_LOADING(false));
});
};
export const logout = async (dispatch, navigator) => {
dispatch(SET_LOADING(true));
await AsyncStorage.removeItem('token');
navigator.navigate('Login');
dispatch(SET_LOADING(false));
};
// HERE I LOGOUT FROM MY SETTINGS SCREEN (WHEN I'M AUTHENTICATED).
正如您所看到的错误(前几行代码),我无法从我的应用程序中正确 navigate/logout。任何帮助将不胜感激!
从外观上看,您的注销操作中有两件事需要调整。
- 您正在从 AsyncStorage 中删除令牌,但您仍然在
useAuth
挂钩中设置了一个用户对象,这是有条件地呈现包含 login/register 的堆栈组或带有 home/settings:
{!user ? (
<Stack.Group>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
</Stack.Group>
) : (
<Stack.Group>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Group>
)}
所以用户不是虚假的并且仍然会呈现 home/settings 因此你不会导航回登录。调度一个操作将其设置回 null,您将自动导航回登录。
- 当您使用 react-navigation 时使用的典型注销流程意味着当您执行注销操作时,您实际上不需要执行
navigator.navigate('Login')
因为该堆栈的默认路由将会返回到登录(只需在导航器中设置它),因为由于我上面提到的第一点,屏幕当前未呈现(有条件地呈现它们,屏幕在您导航时实际上不存在)所以你可以安全地删除此行。
我有一个应用程序,我可以在其中 login/register 并且可以 signout/logout,我正在使用 react navigation v6
进行相同的操作,我可以登录,但是当我注销时没有立即注销我,我将不得不 hot restart
该应用程序,然后我已经注销了,它只是在没有热重启的情况下不注销我,同样,在注销时,我得到一个 error/warning:
error
:
The action 'NAVIGATE' with payload {"name":"Login"} was not handled by any navigator.
Do you have a screen named 'Login'?
If you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.
This is a development-only warning and won't be shown in production.
下面是我使用的代码:
上下文文件:
const initialState = {
user: null,
loading: false,
};
const UserContext = createContext(initialState);
export const UserProvider = ({children}) => {
const [globalState, dispatch] = useReducer(UserReducer, initialState);
return (
<UserContext.Provider value={{
user: globalState.user,
loading: globalState.loading,
dispatch,
}} >
{children}
</UserContext.Provider>
);
};
export default function() {
return useContext(UserContext);
}
导航画面:
const Navigation = () => {
const {user, dispatch, loading} = useAuth();
let navigationRef;
useEffect(() => {
setTimeout(() => {
navigationRef = createNavigationContainerRef(); // To avoid getting error: navigator is undefined while the component is mounting...
}, 500);
checkUserLoggedInStatus(dispatch, navigationRef);
}, [dispatch]);
return loading ? (
<LoadingScreen />
) : (
<Stack.Navigator screenOptions={{headerShown: false}}>
{!user ? (
<Stack.Group>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
</Stack.Group>
) : (
<Stack.Group>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Group>
)}
</Stack.Navigator>
);
};
checkUserLoggedInStatus
、login
& logout
效用函数:
export const login = (otp, userId, dispatch) => {
dispatch(SET_LOADING(true));
fetch(`${API_URL}/auth/login/`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({...}),
})
.then(response => response.json())
.then(data => {
if (data.error) {
console.log('[ERROR] While trying to login: ', data.error);
dispatch(SET_LOADING(false));
}
if (data.type === 'success') {
AsyncStorage.setItem('token', data.data.token)
.then(response => console.log(response))
.catch(err => console.log(err));
const userDetails = data.data.user;
dispatch(SET_USER_DETAILS(userDetails));
dispatch(SET_LOADING(false));
}
})
.catch(error => {
console.log('[ERROR] While logging in: ' + error.message);
dispatch(SET_LOADING(false));
});
};
export const checkUserLoggedInStatus = (dispatch, navigator) => {
dispatch(SET_LOADING(true));
AsyncStorage.getItem('token')
.then(token => {
if (token) {
fetch(`${API_URL}/user/`, {
method: 'GET',
headers: {...},
})
.then(response => response.json())
.then(data => {
if (data.type === 'success') {
const details = data.data.user;
dispatch(SET_USER_DETAILS(details));
dispatch(SET_LOADING(false));
}
if (data.error) {
console.log('[INFO] Your token expired.');
if (navigator.isReady()) {
navigator.navigate('Login');
}
}
})
.catch(error => {
console.log('[ERROR] While fetching profile: ' + error.message);
dispatch(SET_LOADING(false));
});
} else {
dispatch(SET_LOADING(false));
}
})
.catch(error => {
console.log('[ERROR] While fetching token: ' + error.message);
dispatch(SET_LOADING(false));
});
};
export const logout = async (dispatch, navigator) => {
dispatch(SET_LOADING(true));
await AsyncStorage.removeItem('token');
navigator.navigate('Login');
dispatch(SET_LOADING(false));
};
// HERE I LOGOUT FROM MY SETTINGS SCREEN (WHEN I'M AUTHENTICATED).
正如您所看到的错误(前几行代码),我无法从我的应用程序中正确 navigate/logout。任何帮助将不胜感激!
从外观上看,您的注销操作中有两件事需要调整。
- 您正在从 AsyncStorage 中删除令牌,但您仍然在
useAuth
挂钩中设置了一个用户对象,这是有条件地呈现包含 login/register 的堆栈组或带有 home/settings:
{!user ? (
<Stack.Group>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
</Stack.Group>
) : (
<Stack.Group>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Group>
)}
所以用户不是虚假的并且仍然会呈现 home/settings 因此你不会导航回登录。调度一个操作将其设置回 null,您将自动导航回登录。
- 当您使用 react-navigation 时使用的典型注销流程意味着当您执行注销操作时,您实际上不需要执行
navigator.navigate('Login')
因为该堆栈的默认路由将会返回到登录(只需在导航器中设置它),因为由于我上面提到的第一点,屏幕当前未呈现(有条件地呈现它们,屏幕在您导航时实际上不存在)所以你可以安全地删除此行。