在我的 App.js 中,“{isAuthenticated ? ( ExpensesOverview) : ( LoginScreen)” 的逻辑没有按我希望的那样工作。控制台日志显示已生成令牌,但 isAuthenticated 仍为 false。为什么我必须重新加载应用程序以便 isAuthenticated 为 true 并且 ExpensesOverview 屏幕最终呈现?
我想要的是在expense-context.js 中管理整个应用程序的isAuthenticated 状态。 App.js、LoginScreen.js和expenses-context.js的相关部分如下:
App.js:
function ExpensesOverview() {
return (
...
<BottomTabs.Screen
name='RecentExpenses'
component={RecentExpenses}
/>
<BottomTabs.Screen
name='AllExpenses'
component={AllExpenses}
/>
</BottomTabs.Navigator>
)
}
export default function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
// Check if the user is authenticated (e.g., token exists)
const checkAuthentication = async () => {
try {
const token = await AsyncStorage.getItem('userToken');
console.log('Token from AsyncStorage:', token); // Log the token value
if (token) {
setIsAuthenticated(true);
}
} catch (error) {
console.error('Error checking authentication:', error);
// setIsAuthenticated(false); // Set to false on error
}
};
checkAuthentication();
}, []);
return (
<>
<StatusBar style="dark" />
<ExpensesContextProvider>
<NavigationContainer>
<Stack.Navigator>
{isAuthenticated ? (
<>
<Stack.Screen name='ExpensesOverview' component={ExpensesOverview} options={{ headerShown: false }} />
<Stack.Screen name='ManageExpense' component={ManageExpense} options={{
presentation: 'modal'
}} />
</>
) : (
<Stack.Screen name="LoginScreen" component={LoginScreen} options={{ headerShown: false }} />
)}
</Stack.Navigator>
</NavigationContainer>
</ExpensesContextProvider>
</>
);
}
登录屏幕.js:
function LoginScreen() {
const expensesCtx = useContext(ExpensesContext);
const [credentials, setCredentials] = useState({ email: '', password: '' }); // Initialize the credentials state
async function loginHandler({ email, password }) {
try {
const response = await expensesCtx.loginUser({ email, password });
console.log('Response:', response.token);
if (response) {
const {token} = response;
console.log('LoginScreen token:', token);
await AsyncStorage.setItem('userToken', JSON.stringify(token));
await expensesCtx.updateIsAuthenticated(true); // Set isAuthenticated using the context function
} else {
console.error('Invalid response data:', response);
// Handle the case where the response data is not as expected
}
} catch (error) {
console.error('Error logging in:', error);
}
}
return (
<SafeAreaView style={styles.container}>
<AuthContent isLogin={true} onSignUp={signUpHandler} onLogin={loginHandler} />
<View style={styles.registerButtonContainer}>
</View>
</SafeAreaView>
);
}
export default LoginScreen;
expenses-context.js:
export const ExpensesContext = createContext({
loginUser: ({ email, password }) => { }, // New loginUser function
isAuthenticated: false, // Add an initial isAuthenticated state
})
function expensesReducer(state, action) {
case 'LOGIN_USER':
// Add the new user to the state (if needed)
// return [{ ...action.payload }, ...state];
return {
...state,
isAuthenticated: true,
}
default:
return state;
}
}
function ExpensesContextProvider({ children }) {
const [expensesState, dispatch] = useReducer(expensesReducer, []);
// auth status
const [isAuthenticated, setIsAuthenticated] = useState(false)
// check user auth and update the state
useEffect(()=>{
const checkAuthentication = async () => {
try {
const token = await AsyncStorage.getItem('email')
if (token) {
setIsAuthenticated(true)
}
} catch (error) {
console.error('Error checking authentication:', error)
}
}
checkAuthentication()
}, [])
const updateIsAuthenticated = (value) => {
setIsAuthenticated(value);
};
// loginUser: log in a user
async function loginUser({ email, password }) {
try {
const response = await axios.post('http://localhost:8082/authUser', {
email,
password,
});
const { token } = response.data;
await AsyncStorage.setItem('userToken', JSON.stringify(token));
setIsAuthenticated(true); // Set isAuthenticated to true upon successful login
dispatch({ type: 'LOGIN_USER', payload: response.data.token });
return response.data
} catch (error) {
console.error('Error logging in:', error);
}
}
const value = {
loginUser: loginUser, // Include the loginUser function
isAuthenticated: isAuthenticated, // Include the isAuthenticated state
updateIsAuthenticated: updateIsAuthenticated,
};
return (
<ExpensesContext.Provider value={value}>
{children}
</ExpensesContext.Provider>
);
}
export default ExpensesContextProvider;
您有两种
isAuthenticated
状态,一种处于 App.js
,另一种处于 expenses-context.js
。你更新了登录页面的最后一条,但你只是根据里面的状态更新你的路线App.js
。
解决问题的一种方法不会对当前代码进行太大改变,是将
App
包装在上下文提供程序中,如下所示:
// 👇🏽 notice the default export is removed and added back below
function App() {
const { isAuthenticated, updateIsAuthenticated } = useContext(ExpensesContext);
const { checking, setChecking } = useState(true);
useEffect(() => {
// Check if the user is authenticated (e.g., token exists)
const checkAuthentication = async () => {
try {
const token = await AsyncStorage.getItem("userToken");
console.log("Token from AsyncStorage:", token); // Log the token value
if (token) {
updateIsAuthenticated(true);
}
} catch (error) {
console.error("Error checking authentication:", error);
updateIsAuthenticated(false); // Set to false on error
} finally {
setChecking(false);
}
};
checkAuthentication();
}, [updateIsAuthenticated]);
if (checking) {
return "Loading....";
}
return (
<>
<StatusBar style="dark" />
<NavigationContainer>
<Stack.Navigator>
{isAuthenticated ? (
<>
<Stack.Screen
name="ExpensesOverview"
component={ExpensesOverview}
options={{ headerShown: false }}
/>
<Stack.Screen
name="ManageExpense"
component={ManageExpense}
options={{
presentation: "modal",
}}
/>
</>
) : (
<Stack.Screen
name="LoginScreen"
component={LoginScreen}
options={{ headerShown: false }}
/>
)}
</Stack.Navigator>
</NavigationContainer>
</>
);
}
// 👇🏽 the default export is added here
export default function AppWithContext() {
return (
<ExpensesContextProvider>
<App />
</ExpensesContextProvider>
);
}