DEV Community

pcreem
pcreem

Posted on

1 2

Use cache to catch data without page refresh

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

    1. urql: a GraphQL client
    2. @urql/exchange-graphcache: for cache setting
    3. graphql-tag: a utility for parsing GraphQL queries
    4. React / react-bootstrap (optional)
  • GraphQL server

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)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay