Apollo Client Write Query Not Updating UI

前端 未结 2 1813
悲&欢浪女
悲&欢浪女 2021-01-05 09:13

We are building an offline first React Native Application with Apollo Client. Currently I am trying to update the Apollo Cache directly when offline to update the UI optimis

相关标签:
2条回答
  • 2021-01-05 10:02

    If you look at the documentation examples, you will see that they use the data in an immutable way. The data attribute passed to the write query is not the same object as the one that is read. Mutating this object is unlikely to be supported by Apollo because it would not be very efficient for it to detect which attributes you modified, without doing deep copies and comparisons of data before/after.

    const query = gql`
      query MyTodoAppQuery {
        todos {
          id
          text
          completed
        }
      }
    `;
    const data = client.readQuery({ query });
    const myNewTodo = {
      id: '6',
      text: 'Start using Apollo Client.',
      completed: false,
    };
    client.writeQuery({
      query,
      data: {
        todos: [...data.todos, myNewTodo],
      },
    });
    

    So you should try the same code without mutating the data. You can use for example set of lodash/fp to help you

    const data = client.readQuery({...});
    const newData = set("cart.items["+itemIndex+"].quantity",newItemQuantity,data);
    this.props.client.writeQuery({ ..., data: newData });
    

    It recommend ImmerJS for more complex mutations

    0 讨论(0)
  • 2021-01-05 10:04

    Just to save someones time. Using the data in an immutable way was the solution. Agree totally with this answer, but for me I did something else wrong and will show it here. I followed this tutorial and updating the cache worked fine as I finished the tutorial. So I tried to apply the knowledge in my own app, but there the update didn’t work even I did everything similar as showed in the tutorial.

    Here was my approach to update the data using the state to access it in the render method:

    // ... imports
    
    export const GET_POSTS = gql`
        query getPosts {
            posts {
                id
                title
            }
         }
     `
    
    class PostList extends Component {
    
        constructor(props) {
            super(props)
    
            this.state = {
                posts: props.posts
            }
        }
    
        render() {    
            const postItems = this.state.posts.map(item => <PostItem key={item.id} post={item} />)
    
            return (
                <div className="post-list">
                    {postItems}
                </div>
            )
        }
    
    }
    
    const PostListQuery = () => {
        return (
            <Query query={GET_POSTS}>
                {({ loading, error, data }) => {
                    if (loading) {
                        return (<div>Loading...</div>)
                    }
                    if (error) {
                        console.error(error)
                    }
    
                    return (<PostList posts={data.posts} />)
                }}
            </Query>
        )
    }
    
    export default PostListQuery
    

    The solution was just to access the date directly and not using the state at all. See here:

    class PostList extends Component {
    
        render() {
            // use posts directly here in render to make `cache.writeQuery` work. Don't set it via state
            const { posts } = this.props
    
            const postItems = posts.map(item => <PostItem key={item.id} post={item} />)
    
            return (
                <div className="post-list">
                    {postItems}
                </div>
            )
        }
    
    }
    

    Just for completeness here is the input I used to add a new post and update the cache:

    import React, { useState, useRef } from 'react'
    import gql from 'graphql-tag'
    import { Mutation } from 'react-apollo'
    import { GET_POSTS } from './PostList'
    
    const ADD_POST = gql`
    mutation ($post: String!) {
      insert_posts(objects:{title: $post}) {
        affected_rows 
        returning {
          id 
          title
        }
      }
    }
    `
    
    const PostInput = () => {
      const input = useRef(null)
    
      const [postInput, setPostInput] = useState('')
    
      const updateCache = (cache, {data}) => {
        // Fetch the posts from the cache 
        const existingPosts = cache.readQuery({
          query: GET_POSTS
        })
    
        // Add the new post to the cache 
        const newPost = data.insert_posts.returning[0]
    
        // Use writeQuery to update the cache and update ui
        cache.writeQuery({
          query: GET_POSTS,
          data: {
            posts: [
              newPost, ...existingPosts.posts
            ]
          }
        })
    
      }
    
      const resetInput = () => {
        setPostInput('')
        input.current.focus()
      }
    
      return (
        <Mutation mutation={ADD_POST} update={updateCache} onCompleted={resetInput}>
          {(addPost, { loading, data }) => {
            return (
              <form onSubmit={(e) => {
                e.preventDefault()
                addPost({variables: { post: postInput }})
              }}>
                <input 
                  value={postInput}
                  placeholder="Enter a new post"              
                  disabled={loading}
                  ref={input}
                  onChange={e => (setPostInput(e.target.value))}              
                />
              </form>
            )
          }}
        </Mutation>
      )
    }
    
    export default PostInput
    
    0 讨论(0)
提交回复
热议问题