Apollo 客户端写入查询未更新 UI

Dad*_*les 7 javascript apollo reactjs react-native react-apollo

我们正在使用 Apollo Client 构建一个离线的第一个 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 });
Run Code Online (Sandbox Code Playgroud)

Seb*_*ber 13

如果您查看文档示例,您将看到它们以不可变的方式使用数据。传递给写入查询的数据属性与读取的对象不同。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],
  },
});
Run Code Online (Sandbox Code Playgroud)

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

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

它推荐ImmerJS进行更复杂的突变


ron*_*ory 5

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

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

// ... 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
Run Code Online (Sandbox Code Playgroud)

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

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

}
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)