demo
problem aim to solve
In a React todo-list development page, data can't update synchronously with CRUD in the database. Users need to refresh the browser to get the latest information, which made a stuck browser experience. Here gonna use cache, which is a temporary data storage location, and todo-list create/delete to make the problem solve example.
prerequisite
-
GraphQL client
- urql: a GraphQL client
- @urql/exchange-graphcache: for cache setting
- graphql-tag: a utility for parsing GraphQL queries
- React / react-bootstrap (optional)
conceptual code
a. set component, todo create/delete GraphQL queries
code .\src\components\todo.js
import React from 'react'
import gql from 'graphql-tag';
import { useQuery, useMutation } from 'urql';
import { InputGroup, FormControl, Button, Col, Row, Alert, Spinner } from 'react-bootstrap'; //optional
export const FEED_QUERY = gql` //show latest info
query{
info{
id
title
}
}
`;
const UPSERT_POST = gql` //exexute todo create
mutation upsertPost( $postId:ID!, $title: String!) {
upsertPost( postId: $postId, title: $title) {
id
title
}
}
`
const DELETE_POST = gql`
mutation deletePost($postId:ID!) {
deletePost(postId: $postId) {
id
}
}
`
const PostForm = (props) => {
//Upsert Section
let postId = 0
const [title, setTitle] = React.useState('')
const [upsertState, executeUpsert] = useMutation(UPSERT_POST)
const upsert = React.useCallback(() => {
if (title.length !== 0 && title.length <= 30) { executeUpsert({ postId, title }); }
}, [executeUpsert, postId, title])
return (
<Col sm={8}>
<InputGroup className="mb-3">
<FormControl
placeholder='Add Todo...'
aria-label="Recipient's username"
aria-describedby="basic-addon2"
maxLength="30"
value={title}
onChange={e => setTitle(e.target.value)}
/>
<InputGroup.Append>
<Button
variant="outline-secondary"
disabled={upsertState.fetching}
type="submit"
onClick={() => { upsert(); setTitle('') }}
>Add</Button>
</InputGroup.Append>{' '}
</InputGroup>
</Col>
)
}
function Post({ post }) {
//Delete Section
const postId = post.id
const [deleteState, executeDelete] = useMutation(DELETE_POST)
const del = React.useCallback(() => {
executeDelete({ postId })
}, [executeDelete, postId])
return (
<Col >
<Alert variant="light" disabled={deleteState.fetching} onClose={() => { del(); }} dismissible>
<p>{post.title}</p>
</Alert>
</Col>
)
}
const PostList = () => {
const [result] = useQuery({ query: FEED_QUERY })
const { data, fetching, error } = result
if (fetching) return <><Spinner animation="border" role="status"><span className="sr-only">Loading...</span></Spinner></>
if (error) return <div>Error</div>
const postsToRender = data.info
return (
<>
{postsToRender.map(post => <Post key={post.id} post={post} />)}
</>
);
};
const Todo = (props) => {
return (
<>
<br></br>
<Row className="justify-content-md-center">
<PostForm />
</Row>
<hr></hr>
<Row className="justify-content-md-center">
<PostList />
</Row>
</>
)
}
export default Todo
b. cache setting
code .\src\index.js
//...React setting
import { Provider, Client, dedupExchange, fetchExchange } from 'urql'
import { cacheExchange } from '@urql/exchange-graphcache'
import { FEED_QUERY } from './components/todo.js'
const cache = cacheExchange({
updates: {
Mutation: {
upsertPost: (result, args, cache, info) => { //execute create todo
cache.updateQuery({ query: FEED_QUERY }, data => { //update the latest information in cache
if (data !== null) {
data.info.unshift(result.upsertPost);
return data;
} else {
return null
}
});
},
deletePost: (result, args, cache, info) => { // execute delete todo
cache.invalidate({ __typename: 'Post', id: result.deletePost.id }); //delete todo in cache
},
},
},
});
const client = new Client({
url: '/',
fetchOptions: () => {
//...your fetch setting
},
exchanges: [dedupExchange, cache, fetchExchange],
})
ReactDOM.render(
<Provider value={client}>
<App />
</Provider>,
document.getElementById('root')
)
serviceWorker.unregister();
note
The todo-list will get user-related data in the demo, code is not shown here. You could write a todo-only code to experience the cache effect.
Top comments (0)