【问题标题】:Apollo Client Write Query Not Updating UIApollo 客户端写入查询未更新 UI
【发布时间】:2018-02-07 10:53:14
【问题描述】:

我们正在使用 Apollo 客户端构建第一个离线 React Native 应用程序。目前我正在尝试在离线时直接更新 Apollo Cache 以乐观地更新 UI。由于我们离线,我们不会尝试在连接为“在线”之前触发突变,但希望 UI 在仍然离线的突变被触发之前反映这些更改。我们正在使用来自http://dev.apollodata.com/core/read-and-write.html#writequery-and-writefragment 的 readQuery / writeQuery API 函数。并且能够通过 Reacotron 查看正在更新的缓存,但是,UI 不会随着此缓存更新的结果而更新。

    const newItemQuantity = existingItemQty + 1;
    const data = this.props.client.readQuery({ query: getCart, variables: { referenceNumber: this.props.activeCartId } });
    data.cart.items[itemIndex].quantity = newItemQuantity;
    this.props.client.writeQuery({ query: getCart, data });

【问题讨论】:

    标签: javascript reactjs react-native apollo react-apollo


    【解决方案1】:

    如果您查看文档示例,您会发现它们以不可变的方式使用数据。传递给写入查询的数据属性与读取的对象不同。 Apollo 不太可能支持对这个对象进行变异,因为如果不对之前/之后的数据进行深度复制和比较,它检测您修改了哪些属性就不会非常有效。

    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],
      },
    });
    

    因此,您应该在不改变数据的情况下尝试相同的代码。您可以使用例如setlodash/fp 来帮助您

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

    它推荐ImmerJS 用于更复杂的突变

    【讨论】:

    • 我很惊讶这个答案没有任何支持!这似乎是一个常见问题,我没有在文档中看到提及,所以我很困惑为什么没有更多人遇到它。
    • 我刚刚遇到了一个问题,即 writeQuery 不会更新 readQuery 相同数据的其他组件。原因正如 Sebastien 所描述的,我没有传播数据。当我第一次将数组数据写入缓存时,它会偷偷将 proto 一起写入,这在调试器中没有显示。因此,如果我下次在没有 proto 的情况下更新数据,它不会更新组件
    • 这也解决了我的问题。令人惊讶的是,在 Apollo 文档中,有一些不应该起作用的反例:apollographql.com/docs/angular/features/cache-updates
    • 这救了我的命!应该得到更多的支持,非常感谢。
    【解决方案2】:

    只是为了节省别人的时间。以不可变的方式使用数据是解决方案。完全同意answer,但对我来说,我做错了其他事情,将在这里展示。我遵循了这个tutorial 并在完成教程后更新缓存工作正常。所以我尝试将这些知识应用到我自己的应用程序中,但即使我按照教程中所示的方式进行了所有类似操作,更新也无法正常工作。

    这是我在渲染方法中使用状态来更新数据的方法:

    // ... 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
    

    解决方案只是直接访问日期而不使用状态。见这里:

    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>
            )
        }
    
    }
    

    为了完整起见,这里是我用来添加新帖子和更新缓存的输入:

    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
    

    【讨论】:

      猜你喜欢
      • 2020-09-29
      • 2021-01-16
      • 2020-03-13
      • 2017-09-19
      • 2023-03-29
      • 2021-06-01
      • 2021-11-26
      • 2021-03-11
      • 2018-11-04
      相关资源
      最近更新 更多