Ruham
Ruham

Reputation: 759

Apollo Client: Component doesn't render after cache update (reactive variables)

Seen similar issues here, but couldn't wrap my mind around those. I'm new to functional components and Apollo Client, and, after reading the documentation, created a component that should render a list of items, then (upon adding), update the cache and read from it, rendering the component again. The result is different, though, and I can't understand why the component is not rendering. Apollo Client and reactive variables are used.

Here's my cache.js:

import { InMemoryCache, makeVar } from "@apollo/client";

export const cache = new InMemoryCache({
  dataIdFromObject: (object) => object.key,
  typePolicies: {
    Query: {
      fields: {
        items: {
          read() {
            return itemsVar();
          },
        },
      },
    },
  },
});

export const itemsVar = makeVar([]);

export default cache;

Then, I have the following component:

import React, { useState, Fragment } from "react";
import { useQuery, useReactiveVar, useMutation } from "@apollo/client";
import GET_ALL_ACTIVE_ITEMS from "../../queries/Item";
import CREATE_ITEM from "../../mutations/Item";

import { itemsVar } from "../../cache";

//Components
import Item from "./Item";

const ItemContainer = () => {
  // this gets the list of items upon component loading
  const { data } = useQuery(GET_ALL_ACTIVE_ITEMS);

  // Input to create the new Item
  const [itemInput, setItemInput] = useState({
    title: "",
  });

  const [createItem] = useMutation(CREATE_ITEM);

  const onAddItemClick = (e) => {
    e.preventDefault();
    createItem({
      variables: {
        itemInput: itemInput,
      },
    }).then((res) => {
      // Gets the result of the created Item and adds to cached
      let resArray = [...res.data.itemInput];
      itemsVar([...itemsVar().getAllActiveItems, resArray]);
    });
  };

  const onChange = (e) => {
    setItemInput({ ...itemInput, [e.target.name]: e.target.value });
  };

  // Sets the cache reactive variable to the data from the backend
  itemsVar(data);
  const items = useReactiveVar(itemsVar);

  return (
    <div>
      <div>
        <div onClick={onAddItemClick} className="primary-button">
          Add item
        </div>
           <input
            type="text"
            name="title"
            value={itemInput.title}
            onChange={onChange}
          />
      </div>
      {items && items.getAllActiveItems.length === 0 ? (
        <p>No items</p>
      ) : (
        <Fragment>
          {items &&
            items.getAllActiveItems.map(({ _id, title }) => (
              <Item key={_id} title={title} />
            ))}
        </Fragment>
      )}
    </div>
  );
};

export default ItemContainer;

This components properly renders the items on loading. Now, even though it creates a new item, it doesn't refresh the component (I assume the cache is not updated too, though not sure).

Get all items query:

import gql from "graphql-tag";

const GET_ALL_ACTIVE_ITEMS = gql`
  query getAllActiveItems() {
    getAllActiveItems() {
      _id
      title
    }
  }
`;

export default GET_ALL_ACTIVE_ITEMS;

Create item query, it creates an item and returns it as an array:

import gql from "graphql-tag";

const CREATE_ITEM = gql`
  mutation createItem($itemInput: CreateItemInput!) {
    createItem(itemInput: $itemInput) {
      _id
      title
    }
  }
`;

export default CREATE_ITEM;

Can you help me understand what am I missing here?

Upvotes: 1

Views: 1984

Answers (1)

AConsumer
AConsumer

Reputation: 2771

You are NOT specifying if you want to update the cache or not in useMutation hook.

The useMutation hook accepts options as well, like this:-

function useMutation<TData = any, TVariables = OperationVariables>(
  mutation: DocumentNode,
  options?: MutationHookOptions<TData, TVariables>,
): MutationTuple<TData, TVariables> {}

So, if you want to update the cache after a mutation occurs, use

const [createItem] = useMutation(CREATE_ITEM,{update: updateCache});



const updateCache = (cache, {data}) => {
    
    // Fetch from the cache
    const existingItems = cache.readQuery({
      query: GET_ALL_ACTIVE_ITEMS
    });
    // Add to the cache
    const newItem = ....
    cache.writeQuery({
      query: GET_ALL_ACTIVE_ITEMS,
      data: {items: [newItem, ...existingItems]}
    });
  };

Upvotes: 2

Related Questions