2

I'm having issues rehydrating a component with new data via query. To set the premise i have two components that are siblings like this:

<Container>
 <Navbar />
 <Login /> || <Home />
</Container

Basically in my login component i have a mutation that runs like this when i hit login:

this.props.mutate({
        variables: { token },
        refetchQueries: () =>  ['isAuthenticated']
    }).then((data) => {
      console.log(data);
      this.props.history.push(`/`);
    })

So the result ends up being that nothing rerenders, i'm console.log()ing in my navigation components to see if the props for it have changed but it hasn't, however in my network tab and cache is the data that it should be returning.

My navigation item component looks like this:

import React, { Component } from "react";
import { Link, withRouter } from "react-router-dom";
import gql from "graphql-tag";
import { graphql, compose } from "react-apollo";
import { withStyles } from "@material-ui/core/styles";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import Button from "@material-ui/core/Button";
import headerLinksStyle from "./HeaderLinksStyle";
import { LockOpen, GetApp, Info } from "@material-ui/icons";

const isLoggedInQuery = gql`
  query isLoggedInQuery {
    token @client
  }
`;

const loggedInMutation = gql`
  mutation authToken($token: String!) {
    assignAuthToken(token: $token) @client {
      token
    }
  }
`;

const userQuery = gql`
  query isAuthenticated {
    currentUser {
      id
      firstName
      lastName
      gender
      location
      password
      email
      permissions {
        id
        name
        displayOrder
        description
      }
      roles {
        id
        name
        description
        displayOrder
      }
    }
  }
`;

class HeaderLinks extends Component {
  logout = () => {
    localStorage.removeItem("auth_token");
    this.props.mutate({
      variables: { token: null }
    });
    this.props.history.push(`/`);
  };

  refetch = () => {
    console.log("refetch")
    this.props.user.refetch();
  }
  render() {
    const { classes } = this.props;
    const { token } = this.props.data;
    const isLoggedIn = token;
    console.log(this.props);
    return (
      <List className={classes.list}>
        {!isLoggedIn && (
          <ListItem className={classes.listItem}>
            <Link to="/login" style={{ color: "inherit" }}>
              <Button color="inherit" className={classes.navLink}>
                <LockOpen className={classes.icon} />Login
              </Button>
            </Link>
          </ListItem>
        )}
        {!isLoggedIn && (
          <ListItem className={classes.listItem}>
            <Link to="/signup" style={{ color: "inherit" }}>
              <Button color="inherit" className={classes.navLink}>
                <GetApp className={classes.icon} />Sign up
              </Button>
            </Link>
          </ListItem>
        )}
        <ListItem className={classes.listItem}>
            <Button color="inherit" className={classes.navLink} onClick={() => { this.refetch();}}>
              <Info className={classes.icon} />About
            </Button>
        </ListItem>
        {isLoggedIn && (
          <ListItem className={classes.listItem}>
            <Button
              color="inherit"
              className={classes.navLink}
              onClick={event => {
                this.logout();
              }}
            >
              <LockOpen className={classes.icon} />Logout
            </Button>
          </ListItem>
        )}
      </List>
    );
  }
}

export default compose(
  graphql(isLoggedInQuery),
  graphql(loggedInMutation),
  graphql(userQuery, { name: "user", options: {fetchPolicy: 'network-only'}}),
  withStyles(headerLinksStyle),
  withRouter
)(HeaderLinks);

Firing this.props.user.refetch() works fine, however my props are coming back with stale information / error or data.

Karan
  • 1,141
  • 2
  • 19
  • 42

2 Answers2

1

This might not answer your question on re-fetching a query, but it's a good practice to use client.resetStore() on a login action. Since you will likely want any results cached in Apollo Store to be re-fetched after the user is logged in.

This should also fix any re-rendering issues on components dependent on your queries data.

Components will only re-render if they are receiving those query results as props.

Pedro Baptista Afonso
  • 1,158
  • 2
  • 10
  • 18
1

Are you using the graphiql tool at all? You should have a query for a currentUser that you import here inside a queries folder. You should also have a Logout mutation in a mutations folder and import that too. This way you can use a cleaner one liner at the end of HeaderLinks like so: export default graphql(mutation)(graphql(query)(HeaderLinks)); And thats just for starters. I recommend refactor like so:

import React, { Component } from 'react';
import { graphql } from 'react-apollo';
import { Link } from 'react-router';
import query from '../queries/CurrentUser';
import mutation from '../mutations/Logout';

class HeaderLinks extends Component {
  onLogoutClick() {
    this.props.mutate({});
  }

  renderButtons() {
    const { loading, user } = this.props.data;
    if (loading) {
      return <div />;
    }
    if (user) {
      return (
        <li>
          <a onClick={this.onLogoutClick.bind(this)}>Logout</a>
        </li>
      );
    } else {
      return (
        <div>
          <li>
            <Link to="/signup">Signup</Link>
          </li>
          <li>
            <Link to="/login">Login</Link>
          </li>
        </div>
      );
    }
  }

  render() {
    return (
      <nav>
        <div className="nav-wrapper">
          <Link to="/" className="brand-logo left">
            Home
          </Link>
          <ul className="right">{this.renderButtons()}</ul>
        </div>
      </nav>
    );
  }
}

export default graphql(mutation)(graphql(query)(Header));

Now the above is assuming materialize css for styling, you may be using something else and that's fine, just have to reflect that, but I think what I shared above is cleaner and gets you going.

So after I run the mutation I will say that I want GraphQL to automatically refetch this list of queries even if its just one query, but I am going to pass in an array because thats what the helper expects me to do like so:

class HeaderLinks extends Component {
  onLogoutClick() {
    this.props.mutate({
      refetchQueries: [{}]
    });
  }

So I just have one single query I want to re run it is the currentUser query which I imported as query and I will define it like so:

class HeaderLinks extends Component {
  onLogoutClick() {
    this.props.mutate({
      refetchQueries: [{ query: query }]
    });
  }

Key value identical variable names so we can condense it down to just be query like so:

class HeaderLinks extends Component {
  onLogoutClick() {
    this.props.mutate({
      refetchQueries: [{ query }]
    });
  }

So after the mutation runs, re run this query and re render any component associated with that query and the header should automatically update on the screen.

Daniel
  • 14,004
  • 16
  • 96
  • 156