Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jimmyn/apollo-client
Apollo React Hooks with automatic cache updates
https://github.com/jimmyn/apollo-client
apollo apollo-react apollo-react-hooks react react-hooks
Last synced: about 4 hours ago
JSON representation
Apollo React Hooks with automatic cache updates
- Host: GitHub
- URL: https://github.com/jimmyn/apollo-client
- Owner: jimmyn
- License: mit
- Created: 2020-01-09T09:03:26.000Z (almost 5 years ago)
- Default Branch: master
- Last Pushed: 2023-01-07T13:32:49.000Z (almost 2 years ago)
- Last Synced: 2024-11-07T12:53:11.496Z (9 days ago)
- Topics: apollo, apollo-react, apollo-react-hooks, react, react-hooks
- Language: TypeScript
- Homepage:
- Size: 1.09 MB
- Stars: 3
- Watchers: 3
- Forks: 0
- Open Issues: 11
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Apollo Client
A drop-in replacement
for [@apollo/client](https://www.apollographql.com/docs/react/get-started/) with automatic cache
updates. It will update apollo cache based on a mutation or subscription result.## Install
```
npm i @jimmyn/apollo-client @apollo/client --save
```or
```
yarn add @jimmyn/apollo-client @apollo/client
```## Setup
```typescript jsx
import React from 'react';
import {render} from 'react-dom';
import {ApolloClient, InMemoryCache} from '@jimmyn/apollo-client';const client = new ApolloClient({
uri: 'localhost:8080',
cache: new InMemoryCache()
});const App = () => (
My first Apollo app 🚀
);render(, document.getElementById('root'));
```## Mutations
This package
extends [useMutation](https://www.apollographql.com/docs/react/api/react/hooks/#options-2) options
allowing to update cached queries in one line of code instead of writing complex `update` functions.For example this code
```typescript jsx
import React from 'react';
import {useMutation, useQuery} from '@jimmyn/apollo-client';
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 (
Create todo
);
};
```is equivalent to
```typescript jsx
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 (
Create todo
);
};
```And this code
```typescript jsx
import React from 'react';
import {useMutation} from '@jimmyn/apollo-client';
import {Todo} from './api/generated';
import {deleteTodoMutation, todosQuery, updateTodoMutation} from './api/operations';type Props = {
todo: Todo;
};export const Todo: React.FC = ({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 (
{todo.task}
delete
);
};
```
is equivalent to
```typescript jsx
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 = ({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 (
{todo.task}
delete
);
};
```
## `useMutation` offline options
| Option | Description | Default |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------- |
| `updateQuery` | A 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}}` |
| `updatePath` | Overrides a path inside the query where the item should be updated. For example if your query has structure like `{items: [...], total: 10}`, you can specify `updatePath: ['items']`. In most cases it is automatically detected. Pass `[]` to update data in the root query object |
| `idField` | Unique field that is used to find the item in cache. It should be present in the mutation response | `id` |
| `operationType` | Indicates 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` |
| `mapResultToUpdate` | A function that receives mutation result and returns an updated item. Function result should contain at least an id field |
[Other `useMutation` hook options](https://www.apollographql.com/docs/react/api/react/hooks/#options-2)
Offline options can be passed to the `useMutation` hook or to the mutation function directly.
```typescript jsx
const [deleteTodo] = useMutation(deleteTodoMutation, {
updateQuery: todosQuery,
mapResultToUpdate: data => todo
});
const handleDeleteTodo = () => {
return deleteTodo({
variables: {id: todo.id}
});
};
```
is the same as
```typescript jsx
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`
```typescript jsx
useSubscription(onTodoUpdate, {updateQuery: todosQuery});
```
[Other `useSubscription` hook options](https://www.apollographql.com/docs/react/api/react/hooks/#options-3)
## Customize default configurations
Default configurations can be customized by calling `setOfflineConfig`
```typescript jsx
import {setOfflineConfig} from '@jimmyn/apollo-client';
setOfflineConfig({
getIdFieldFromObject(item: any) {
switch (item.__typename) {
case 'Todo':
return 'id';
case 'User':
return 'user_id';
}
}
});
```
## Configuration options
| Option | Description | Default |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| `idField` | Unique field that is used to find the item in cache. It should be present in the mutation response | `id` |
| `getIdFieldFromObject` | A function that receives updated item and returns an id field name. If defined it will tke precedence over `idField` |
| `prefixesForRemove` | A list of mutation name prefixes that will result in remove operation | [prefixesForRemove](src/const.ts#L8) |
| `prefixesForUpdate` | A list of mutation name prefixes that will result in update operation | [prefixesForUpdate](src/const.ts#L19) |
| `prefixesForAdd` | A list of mutation name prefixes that will result in add operation | [prefixesForAdd](src/const.ts#L32) |
## Update Apollo cache directly
This package also exposes `updateApolloCache` function directly, that can be used to build custom
implementations
Example
```typescript
import {updateApolloCache} from '@jimmyn/apollo-client';
const newTodo = {
__typename: 'Todo',
id: 1,
task: 'New todo',
done: false,
createdAt: new Date().toISOString()
};
updateApolloCache({
client,
data: {createTodo: newTodo},
updateQuery: todosQuery
});
```
Function signature
```typescript
type OfflineOptions = {
updateQuery?: QueryWithVariables | DocumentNode;
idField?: string;
operationType?: OperationTypes;
mapResultToUpdate?(data: NonNullable): Item;
};
type UpdateCacheOptions = OfflineOptions & {
client: ApolloClient | DataProxy;
data: TData;
};
const updateApolloCache: ({
client,
data,
idField,
updateQuery,
operationType,
mapResultToUpdate
}: UpdateCacheOptions) => void;
```
## Credits
This package is based
on [Amplify Offline Helpers](https://github.com/awslabs/aws-mobile-appsync-sdk-js/blob/master/OFFLINE_HELPERS.md)