Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

apollo-offline-hooks

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

apollo-offline-hooks

Apollo React Hooks with automatic cache updates

  • 1.0.0
  • latest
  • Source
  • npm
  • Socket score

Version published
Maintainers
1
Created
Source

Apollo Offline Hooks

A drop-in replacement for @apollo/react-hooks with automatic cache updates. It will update apollo cache based on a mutation or subscription result.

Install

npm i apollo-offline-hooks @apollo/react-hooks --save

or

yarn add apollo-offline-hooks @apollo/react-hooks

Setup

import React from 'react';
import {render} from 'react-dom';
import ApolloClient from 'apollo-boost';
import {ApolloProvider} from 'apollo-offline-hooks';

const client = new ApolloClient({
  uri: 'localhost:8080',
});

const App = () => (
  <ApolloProvider client={client}>
    <div>
      <h2>My first Apollo app 🚀</h2>
    </div>
  </ApolloProvider>
);

render(<App />, document.getElementById('root'));

Mutations

This package extends useMutation options allowing to update cached queries in one line of code instead of writing complex update functions.

For example this code

import React from 'react';
import {useMutation, useQuery} from 'apollo-offline-hooks';
import {createTodoMutation, todosQuery} from './api/operations';
import {TodosList} from './TodosList';

export const Todos = () => {
  const {data} = useQuery(todosQuery);
  const todos = data?.todos || [];
  
  const [createTodo] = useMutation(createTodoMutation, {
    updateQuery: todosQuery // <== pass a gql query you want to update
  });
  

  const handleCreateTodo = () => {
    return createTodo({
      variables: {
        task: 'New todo',
        createdAt: new Date().toISOString()
      }
    });
  };

  return (
    <div>
      <button onClick={handleCreateTodo}>Create todo</button>
      <TodosList todos={todos} />
    </div>
  );
};

is equivalent to

import React from 'react';
import {useMutation, useQuery} from '@apollo/react-hooks';
import {createTodoMutation, todosQuery} from './api/operations';
import {TodosList} from './TodosList';

export const Todos = () => {
  const {data} = useQuery(todosQuery);
  const todos = data?.todos || [];
  
  const [createTodo] = useMutation(createTodoMutation);
  
  const handleCreateTodo = () => {
    return createTodo({
      variables: {
        task: 'New todo',
        createdAt: new Date().toISOString()
      },
      update: (proxy, {data}) => {
        const newTodo = data.createTodo;
        try {
          const cache = proxy.readQuery({query: todosQuery});
          proxy.writeQuery({
            query: todosQuery,
            data: {
              todos: [...cache.todos, newTodo]
            }
          });
        } catch (error) {
          console.log(error)
        }
      }
    });
  };

  return (
    <div>
      <button onClick={handleCreateTodo}>Create todo</button>
      <TodosList todos={todos} />
    </div>
  );
};

And this code

import React from 'react';
import {useMutation} from 'apollo-offline-hooks';
import {Todo} from './api/generated';
import {deleteTodoMutation, todosQuery, updateTodoMutation} from './api/operations';

type Props = {
  todo: Todo;
};

export const Todo: React.FC<Props> = ({todo}) => {
  const [deleteTodo] = useMutation(deleteTodoMutation, {
    updateQuery: todosQuery,
    
    // to delete an item we need to provide it's id
    // if our api simply returns true when item is deleted
    // we need to return an id explicitly
    mapResultToUpdate: data => todo 
  });
  const [updateTodo] = useMutation(updateTodoMutation);

  const handleDeleteTodo = () => {
    return deleteTodo({
      variables: {id: todo.id}
    });
  };

  const handleUpdateTodo = () => {
    return updateTodo({
      variables: {id: todo.id, done: !todo.done}
    });
  };

  return (
    <li>
      <input type="checkbox" checked={todo.done} onChange={handleUpdateTodo} />
      {todo.task}
      <button onClick={handleDeleteTodo}>delete</button>
    </li>
  );
};

is equivalent to

import React from 'react';
import {useMutation} from '@apollo/react-hooks';
import {Todo} from './api/generated';
import {deleteTodoMutation, todosQuery, updateTodoMutation} from './api/operations';

type Props = {
  todo: Todo;
};

export const Todo: React.FC<Props> = ({todo}) => {
  const [deleteTodo] = useMutation(deleteTodoMutation);
  const [updateTodo] = useMutation(updateTodoMutation);

  const handleDeleteTodo = () => {
    return deleteTodo({
      variables: {id: todo.id},
      update: proxy => {
        try {
          const cache = proxy.readQuery({query: todosQuery});
          proxy.writeQuery({
            query: todosQuery,
            data: {
              todos: cache.todos.filter(item => item.id !== todo.id)
            }
          });
        } catch (error) {
          console.log(error)
        }
      }
    });
  };

  const handleUpdateTodo = () => {
    // apollo client is clever enough to update an item in cache
    // although if you want to update an item with different type you'll have to write
    // a manual update function
    return updateTodo({
      variables: {id: todo.id, done: !todo.done}
    });
  };

  return (
    <li>
      <input type="checkbox" checked={todo.done} onClick={handleUpdateTodo} />
      {todo.task}
      <button onClick={handleDeleteTodo}>delete</button>
    </li>
  );
};

useMutation offline options

OptionDescriptionDefault
updateQueryA graphql query (wrapped in gql tag) that should be updated. You can pass query directly or specify it with variables {query: todosQuery, variables: {limit: 10}}
idFieldUnique field that is used to find the item in cache. It should be present in the mutation responseid
operationTypeIndicates what type of the operation should be performed e.g. add/remove/update item. By default operation type is automatically detected from mutation name e.g. createTodo will result in OperationTypes.ADD.OperationTypes.AUTO
mapResultToUpdateA function that receives mutation result and returns an updated item. Function result should contain at least an id field

Other useMutation hook options

Offline options can be passed to the useMutation hook or to the mutation function directly.

const [deleteTodo] = useMutation(deleteTodoMutation, {
  updateQuery: todosQuery,
  mapResultToUpdate: data => todo
});

const handleDeleteTodo = () => {
  return deleteTodo({
    variables: {id: todo.id}
  });
};

is the same as

const [deleteTodo] = useMutation(deleteTodoMutation);

const handleDeleteTodo = () => {
  return deleteTodo({
    variables: {id: todo.id},
    updateQuery: todosQuery,
    mapResultToUpdate: data => todo
  });
};

Subscriptions

useSubscription accepts the same offline options as useMutation

useSubscription(onTodoUpdate, {updateQuery: todosQuery});

Other useSubscription hook options

Customize default configurations

Default configurations can be customized by calling setOfflineConfig

import {setOfflineConfig} from 'apollo-offline-hooks';

setOfflineConfig({
  getIdFieldFromObject(item: any) {
    switch (item.__typename) {
      case 'Todo':
        return 'id';
      case 'User':
        return 'user_id'
    }
  }
});

Configuration options

OptionDescriptionDefault
idFieldUnique field that is used to find the item in cache. It should be present in the mutation responseid
getIdFieldFromObjectA function that receives updated item and returns an id field name. If defined it will tke precedence over idField
prefixesForRemoveA list of mutation name prefixes that will result in remove operationprefixesForRemove
prefixesForUpdateA list of mutation name prefixes that will result in update operationprefixesForUpdate
prefixesForAddA list of mutation name prefixes that will result in add operationprefixesForAdd

Update Apollo cache directly

This package also exposes updateApolloCache function directly, that can be used to build custom implementations

Example

import {updateApolloCache} from 'apollo-offline-hooks';

const newTodo = {
  __typename: 'Todo',
  id: 1,
  task: 'New todo',
  done: false,
  createdAt: new Date().toISOString()
};

updateApolloCache({
  client,
  data: {createTodo: newTodo},
  updateQuery: todosQuery
});

Function signature

type OfflineOptions<TData> = {
    updateQuery?: QueryWithVariables | DocumentNode;
    idField?: string;
    operationType?: OperationTypes;
    mapResultToUpdate?(data: NonNullable<TData>): Item;
};

type UpdateCacheOptions<TData = any> = OfflineOptions<TData> & {
    client: ApolloClient<any> | DataProxy;
    data: TData;
};

const updateApolloCache: <TData = any>({ 
    client, 
    data, 
    idField, 
    updateQuery, 
    operationType, 
    mapResultToUpdate
}: UpdateCacheOptions<TData>) => void;

Credits

This package is based on Amplify Offline Helpers

Keywords

FAQs

Package last updated on 20 Jul 2020

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc