DEV Community

Kuldeep Bora
Kuldeep Bora

Posted on • Updated on

React Todo App with Apollo client (local state)

I find Apollo official documentation hard to follow as it's quite lengthy and bit confusing to figure out the big picture at first. Luckily there are other articles/tutorials which are much better to follow. Below is my attempt to understand and write a classic Todo app with Apollo client.

Assuming that required dependencies are installed.

Step 1. Create Apollo client

First step is to initialize the apollo client which will take take of our local state and it's mutations.

const cache = new InMemoryCache();
const client = new ApolloClient({
  cache,
  resolvers
});

Cache is our local storage. Resolvers are our functions that will actually resolve the query we send to apollo. We will see them later. The client will also take 'link' as optional param for remote data fetching but we do not need it here.

Step 2. Initialize local state

cache.writeData({
  data: {
    todos: []
  }
});

Use cache.writeData to write data in local storage as initial value. This is same as setState = { todos: [] } or useState({todos: []}).

Step 3. Define queries.

import { gql } from "apollo-boost";

export const GET_TODOS = gql`
  {
    todos @client {
      id
      name
      completed
    }
  }
`;

export const ADD_TODO = gql`
  mutation addTodo($name: String!) {
    addTodo(name: $name) @client {
      name
    }
  }
`;

These are needed to get and add todo in local storage (cache). These will be used by resolver to get exact data we asked. Note that to add a todo, we just have to send the todo name.

Step 4. Provide resolvers.

export const resolvers = {
  Mutation: {
    addTodo: (_, todo, { cache }) => {
      const { todos } = cache.readQuery({ query: GET_TODOS });
      let new_todo = {
        id: RandomID(10),
        name: todo.name,
        completed: false,
        __typename: "todo"
      };
      cache.writeData({
        data: {
          todos: [...todos, new_todo]
        }
      });

      return new_todo;
    }
  }
};

Resolvers are needed to tell graphql what to do when called (via useMutation here). Note that I didn't provide resolver for read Todo. The apollo client will directly look in cache if a local resolver is not provided for the query.

Step 5. Use UseQuery() and UseMutation() to read and write data to local storage and display in UI

function App() {
  const [task, setTask] = useState("");
  const { loading, error, data } = useQuery(GET_TODOS);
  const [addTodo] = useMutation(ADD_TODO);

  if (loading) return <h2> Loading... </h2>;
  if (error) return <h2> ERROR...: {error} </h2>;
  return (
    <div className="App">
      <h1>TODOS:</h1>
      {data.todos.map(todo => (
        <li key={todo.id}> {todo.name} </li>
      ))}
      <input
        placeholder="add a Todo"
        onChange={e => setTask(e.target.value)}
        value={task}
      />
      <button
        onClick={() => {
          setTask("");
          addTodo({
            variables: {
              name: task
            }
          });
        }}
      >
        Add
      </button>
    </div>
  );
}

Final step is to read and write data from UI itself.

code link: https://codesandbox.io/s/react-apollo-todo-local-state-j4c7c

Note that we don't need the schema here because we are only using primitives (string) to read and write.

That's it for now. I am still in search for better articles and documentation for Apollo to understand it more. If anyone come across better articles, please do let me know. Thanks a lot. Happy learning.

P.S. Please do correct me if something is inaccurate or incomplete above.

Top comments (0)