我正在使用React和Graphql和Apollo。
我的index.js如下:
const client = new ApolloClient({
uri: GRAPHQL_URL,
fetchOptions: {
credentials: 'include'
},
request: (operation) => {
const token = localStorage.getItem(AUTH_TOKEN_KEY) || "";
operation.setContext({
headers: {
Authorization: `JWT ${token}`
}
})
},
clientState: {
defaults: {
isLoggedIn: !!localStorage.getItem(AUTH_TOKEN_KEY)
}
}
});
const IS_LOGGED_IN_QUERY = gql`
query {
isLoggedIn @client
}
`;
ReactDOM.render(
<ApolloProvider client={client}>
<Query query={IS_LOGGED_IN_QUERY}>
{({data}) => {
return data.isLoggedIn ? <App/> : <Login/>
}}
</Query>
</ApolloProvider>,
document.getElementById('root')
);
因此,如果用户登录,我将令牌保存到localStorage
,并且显示了<App />
,而不是<Login />
<Login />
如下:
const Login = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [logIn, { loading: mutationLoading, error: mutationError }] = useMutation(LOG_IN_MUTATION);
const client = useApolloClient();
const handleSubmit = async (e) => {
e.preventDefault();
const res = await logIn({variables: {username, password}});
client.writeData({ data: { isLoggedIn: true } })
localStorage.setItem(AUTH_TOKEN_KEY, res.data.tokenAuth.token);
};
return ( ... )
在<App />
中,我对后端执行Graphql查询,以获取当前登录用户的数据。滞留在后端的代码按预期工作。
<App />
如下:
function App() {
const {loading, error, data} = useQuery(GET_ME);
if (loading) return <div><LoadingIndicator1/></div>
if (!loading && error) return <div ><ErrorIndicator/></div>
return (
...
);
}
export default App;
GET_ME
查询如下:
export const GET_ME = gql`
{
me{
id
username
firstName
lastName
email
}
}
`;
注销功能如下:
const client = useApolloClient();
const signOut = (client) => {
localStorage.removeItem(AUTH_TOKEN_KEY);
client.writeData({
data: {
isLoggedIn: false
}
})
};
但是问题是,当我以一个用户身份登录并注销,然后以另一用户身份登录时,我仍然看到旧的用户身份。如果再刷新,我会看到新用户。
知道我做错了什么吗?
更新
<App />
组件如下:
function App() {
const {loading, error, data} = useQuery(GET_ME);
if (loading) return <div><LoadingIndicator1/></div>
if (!loading && error) return <div ><ErrorIndicator/></div>
return (
<Router>
<UserContext.Provider value={data.me}>
<ToastProvider>
<Header/>
<Switch>
<Route path="/report/:id">
<NewReport/>
</Route>
<Route exact path="/report/">
<NewReport/>
</Route>
<Route path="/reports/">
<Reports/>
</Route>
<Route exact path="/">
<Home/>
</Route>
</Switch>
</ToastProvider>
</UserContext.Provider>
</Router>
);
}
并且我使用用户详细信息的组件如下:
render (
<div>
{localStorage.getItem(AUTH_TOKEN_KEY) !== null && <div>
<div className=''>
<span>{currentUser.username}</span>
<i className="fad fa-angle-down" />
</div>
</div>}
</div>
)
我在主要组件()中执行此操作,然后通过React Context将其传递给所有子组件。
我想在一个地方做,并将其作为道具传下来。
使用上下文复制了已经存在的apollo客户端上下文。您可以简单地通过useQuery
获得相同的信息(如果需要,可以使用cache-only
)。
其他提示:
<Query query={IS_LOGGED_IN_QUERY}>
)应该是FC(带有useQuery
钩子)并合并到<App/>
组件中;me
,则用户已登录(不必要的状态复制)-您可以将它们合并为一个查询;...可能是由GET_ME
查询的缓存结果引起的。既不是network-only
也没有通过某些用户ID变量进行参数设置。
来自Apollo文档(auth):
确保UI和存储状态反映当前用户权限的最简单方法是在登录或注销过程完成后调用
client.resetStore()
。