【发布时间】:2021-05-01 18:10:28
【问题描述】:
我正在尝试找出在我的 React 应用程序中处理身份验证和路由的最佳方法。我使用了Kent Dodds 和also here 概述的方法,其他人扩展了他的方法。
基本上,您有一个用于身份验证的上下文和一个用于经过身份验证的用户信息的上下文。如果用户通过身份验证,则呈现<AuthenticatedApp />,如果不存在用户信息,则呈现<UnauthenticatedApp/>。
各个供应商的细分如下:
AppContext.js
const AppProvider = ({ children }) => (
<Router>
<ApolloProvider client={client}>
<AuthProvider>
<UserProvider>{children}</UserProvider>
</AuthProvider>
</ApolloProvider>
</Router>
);
export default AppProvider;
还有我的App.js,
function App() {
const user = useUser();
return user ? (
<ThemeProvider theme={theme}>
<GlobalStyle />
<AuthenticatedApp />
</ThemeProvider>
) : (
<ThemeProvider theme={theme}>
<GlobalStyle />
<UnauthenticatedApp />
</ThemeProvider>
);
}
如何最好地处理未验证组件和已验证组件中的各个组件之间的路由?我应该将它们单独包装在<Router> 中还是让<Router 围绕在我的上下文上方的顶层?我目前在其他上下文提供者之上使用第一种方法和反应路由器。
AuthenticatedApp.js
const RedirectHome = () => <Redirect to="/dashboard" />;
const AuthenticatedApp = () => (
<Switch>
<Route exact path="/">
<RedirectHome />
</Route>
<Route path="/dashboard">
<DashboardNavbar />
<Dashboard />
</Route>
<Route path="/projects">
<DashboardNavbar />
<Projects />
</Route>
<Route exact path="/*">
<RedirectHome />
</Route>
</Switch>
);
UnauthenticatedApp.js
<Switch>
<Route exact path="/">
<LandingPage RightSide={UserLogin} />
</Route>
<Route exact path="/login">
<LandingPage RightSide={UserLogin} />
</Route>
<Route path="/signup">
<LandingPage RightSide={UserSignup} />
</Route>
<Route path="/*">
<LandingPage RightSide={UserLogin} />
</Route>
</Switch>
这是最好的方法吗?为了注销,我有以下内容:
AuthContext.js
const history = useHistory();
const logout = () => {
localStorage.removeItem('AUTH_TOKEN');
refetch();
history.push('/');
history.go();
};
将历史记录推送到/更改地址但似乎没有更改页面,它仍然位于仪表板页面上,即使删除了 JWT 令牌,我仍然没有收到 <Unauthenticated/> 组件。在发现身份验证缺少 JWT 令牌之前,我必须强制重新加载当前页面。
对不起,我只是很困惑!
更新 #1
我的 useUser 上下文/钩子是:
const UserProvider = (props) => {
const { data } = useAuth();
return (
// eslint-disable-next-line react/jsx-props-no-spreading
<UserContext.Provider value={data ? data.getWhoAmI : null} {...props} />
);
};
const useUser = () => React.useContext(UserContext);
export { UserProvider, useUser };
我的 AuthContext 是:
const AuthContext = React.createContext();
const AuthProvider = (props) => {
const { loading, data, refetch } = useQuery(WHOAMI_QUERY);
const [loginUser] = useMutation(LOGIN_USER_MUTATION);
const [signupUser] = useMutation(SIGNUP_USER_MUTATION);
const signin = async (username, password) =>
loginUser({ variables: { username, password } }).then((res) => {
if (res && res.data && res.data.loginUser && res.data.loginUser.token) {
const { token } = res.data.loginUser;
localStorage.setItem('AUTH_TOKEN', token);
refetch();
} else {
throw Error('No token returned');
}
return res;
});
const signup = (firstName, lastName, username, email, password) =>
signupUser({
variables: { firstName, lastName, username, email, password },
}).then((res) => {
if (res && res.data && res.data.signup && res.data.signup.token) {
const { token } = res.data.signup;
localStorage.setItem('AUTH_TOKEN', token);
refetch();
} else {
throw Error('No token returned');
}
return res;
});
const history = useHistory();
const logout = () => {
localStorage.removeItem('AUTH_TOKEN');
refetch();
history.push('/');
history.go();
};
if (loading) {
return <p>Loading!</p>;
}
return (
// eslint-disable-next-line react/jsx-props-no-spreading
<AuthContext.Provider value={{ data, signin, signup, logout }} {...props} />
);
};
const useAuth = () => React.useContext(AuthContext);
export { AuthProvider, useAuth };
【问题讨论】:
-
乍一看,这里没有什么明显的错误。您带有一个
Router和两个Switch块的结构看起来应该没问题。所以我不确定错误在哪里。 -
如果我们从“/login”重定向到“/”,这似乎可行。我们是否从“/”(注销的地方)重定向到“/”(登录的地方)?由于路径相同,可能不会触发刷新。
-
这就是我正在努力解决的问题。我原以为推动历史更改就足够了,但如果只需要在令牌删除和 history.push 之后重新加载页面,那么也许这是一个可以接受的解决方案?只是感觉很笨重。
-
我已更新 AuthenticatedApp 以显示重定向。登录时,“/”重定向到“/dashboard”。因此,如果我在 /dashboard 上并单击注销,则会删除 JWT 令牌,然后调用 history.push("/"),这将导致我被重定向回“/dashboard”,但没有存储用户 / JWT所以应该渲染 UnauthenticatedApp 组件,带我回到登录页面。
-
history.push("/")会将他们重定向到 URL"/",不是吗?
标签: reactjs react-router react-context