0

I'm using React and Graphql and Apollo.

My index.js is as follows:

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')
);

Thus if user is logged in I save token to localStorage and the <App /> is shown instead of <Login />

The <Login /> is as follows:

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 ( ... )

In de <App /> I do a Graphql query to the backend to get de data of currently logged in user. That stuck code in the backend works as expected.

The <App /> is as follows:

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;

And the GET_ME query is as follows:

export const GET_ME = gql`
    {
        me{
            id
            username
            firstName
            lastName
            email
        }
    }
`;

The function for logging out is as follows:

const client = useApolloClient();

    const signOut = (client) => {
        localStorage.removeItem(AUTH_TOKEN_KEY);
        client.writeData({
            data: {
                isLoggedIn: false
            }
        })
    };

But the problem is when I log in with one user and the logout, then login with the other user, i still see the old one. If I then refresh I see the new user.

Any idea what I do wrong?

UPDATE

The <App /> component is as follows:

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

And component where I use the user details is as follows:

render (
    <div>
        {localStorage.getItem(AUTH_TOKEN_KEY) !== null && <div>
                        <div className=''>
                           <span>{currentUser.username}</span>
                            <i className="fad fa-angle-down" />
                        </div>
                    </div>}
    </div>
)
xadm
  • 6,900
  • 3
  • 8
  • 17
Boky
  • 8,942
  • 19
  • 69
  • 134

1 Answers1

2

I do it in main component () and then pass it via React Context to all child components.

I want to do it on one place and pass it down as props.

Using context duplicates already existing apollo client context. You can simply useQuery to get the same (cache-only if you want).

Other hints:

  • 'main' query (<Query query={IS_LOGGED_IN_QUERY}>) should be a FC (with useQuery hook) and combined into <App/> component;
  • if me is defined then user is logged in (unnecessary state duplication) - you can combine them in one query;
  • more common router auth/private/proteced routes?
  • outdated methods (apollo local state, router)

The main problem

... probably caused by cached results of GET_ME query. It's not network-only neither parametrized by some user id variable.

From Apollo docs (auth):

The easiest way to ensure that the UI and store state reflects the current user's permissions is to call client.resetStore() after your login or logout process has completed.

Community
  • 1
  • 1
xadm
  • 6,900
  • 3
  • 8
  • 17