React Routing with Authentication and User Contexts



我正在努力找出在我的React应用程序中处理身份验证和路由的最佳方法。我使用了肯特·多兹概述的方法,这里也有其他人对他的方法进行了扩展。

基本上,您有一个用于身份验证的上下文和一个用于已验证的用户信息的上下文。如果用户通过身份验证,则呈现<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?我目前使用的第一种方法是在我的顶层使用react 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令牌之前,我必须强制重新加载当前页面。

很抱歉,我只是很困惑!

Update # 1

我的useUser context/hook是:

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 };

在你的App.js文件中没有状态变化,所以在你的用户注销后它不会被重新渲染。你必须使用useEffect钩子或者切换到一个基于类的组件来包含state。

下面的代码展示了他们是如何通过props传递login函数来设置App.js中的状态的。注销函数的工作原理与此类似。我已经在我的网站上做了类似的事情。

因此,在深入研究GraphQL查询getWhoAmI之后,似乎当我注销在getWhoAmI查询上调用refetch()时,它无法使用JWT来验证请求并找到用户,然后执行不更新{data}的throw new AuthenticationError('You do not have permission for this request');,但奇怪的是不返回{错误}。

如果我将GraphQL解析器更改为返回null而不是错误,那么我将注销,因为{data}从用户信息更改为null,这会导致重新渲染。

最新更新