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

时间:2017-08-29 21:05:04

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

我们正在使用Apollo Client构建离线的第一个React Native Application。目前我正在尝试在离线时直接更新Apollo Cache以乐观地更新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 });

2 个答案:

答案 0 :(得分:2)

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

这是我使用状态在render方法中访问数据来更新数据的方法:

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

答案 1 :(得分:0)

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

因此,您应该在不改变数据的情况下尝试相同的代码。您可以使用set的{​​{1}}来帮助您

lodash/fp