Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I add computed state to graph objects in React Apollo?

I really like the graphQL pattern of having components request their own data, but some data properties are expensive to compute and so I want to localize the logic (and code) to do so.

function CheaterList({ data: { PlayerList: players } }) {
  return (
    <ul>
      {players && players.map(({ name, isCheater }) => (
        <li key={name}>{name} seems to be a {isCheater ? 'cheater' : 'normal player'}</li>
      ))}
    </ul>
  );
}

export default graphql(gql`
  query GetList {
    PlayerList {
      name,
      isCheater
    }
  }
`)(CheaterList);

The schema looks like:

type Queries {
    PlayerList: [Player]
}

type Player {
    name: String,
    kills: Integer,
    deaths: Integer
}

And so I want to add the isCheater property to Player and have its code be:

function computeIsCheater(player: Player){
    // This is a simplified version of what it actually is for the sake of the example
    return player.deaths == 0 || (player.kills / player.deaths) > 20;
}

How would I do that?

Another way of phrasing this would be: how do I get the isCheater property to look as though it came from the backend? (However, if an optimistic update were applied the function should rerun on the new data)

like image 841
chacham15 Avatar asked Mar 06 '23 20:03

chacham15


1 Answers

Note: Local state management is now baked into apollo-client -- there's no need to add a separate link in order to make use of local resolvers and the @client directive. For an example of mixing local and remote fields, check out of the docs. Original answer follows.

As long as you're using Apollo 2.0, this should be possible by utilizing apollo-link-state as outlined in the docs.

Modify your client configuration to include apollo-link-state:

import { withClientState } from 'apollo-link-state';

const stateLink = withClientState({
  cache, //same cache object you pass to the client constructor
  resolvers: linkStateResolvers,
});

const client = new ApolloClient({
  cache,
  link: ApolloLink.from([stateLink, new HttpLink()]),
});

Define your client-only resolvers:

const linkStateResolvers = {
  Player: {
    isCheater: (player, args, ctx) => {
      return player.deaths == 0 || (player.kills / player.deaths) > 20
    }
  }
}

Use the @client directive in your query

export default graphql(gql`
  query GetList {
    PlayerList {
      name
      kills
      deaths
      isCheater @client
    }
  }
`)(CheaterList);

However, it appears that combining local and remote fields in a single query is currently broken. There's an open issue here that you can track.

like image 179
Daniel Rearden Avatar answered Mar 25 '23 07:03

Daniel Rearden