如何在 React Apollo 2.0 中正确地重新获取和缓存更新的数据?

Posted

技术标签:

【中文标题】如何在 React Apollo 2.0 中正确地重新获取和缓存更新的数据?【英文标题】:How do I properly refetch and cache updated data in React Apollo 2.0? 【发布时间】:2018-09-23 07:03:07 【问题描述】:

我一直在尝试使用从我的服务器重新获取的最新数据来更新我的缓存,但由于某种原因,我所有的旧数据都会一直保留,直到我重新启动服务器或执行类似 client.resetStore() 之类的操作。

当我第一次启动我的应用程序时,它会在客户端缓存中存储一​​封电子邮件。然后使用这个缓存的电子邮件,它被传递到服务器端查询以获取有关用户的数据。然后我通过 props 访问这些数据并将我的 UI 渲染成类似“Welcome Ken.”的内容。但是......如果我进入我的数据库并手动将名称更改为..“Nek”,然后点击刷新按钮,我期待一个“Welcome Nek”。但相反,它仍然是“肯”。我可以看到在我的服务器端从数据库中检索到了正确的数据,但是当我使用 Apollo devtools 查看我的缓存时,我可以看到它仍然是以前的数据并且尚未使用新获取的数据进行更新。

在我在数据库中手动更改名称后,我在底部包含了一个日志。我唯一改变的是“first_name”字段从“Ken”到“Nek”就是这样。

除了尝试使用 refetch()、refetchQueries、轮询...我还尝试执行另一个突变,只是为了查看它是否会在重新渲染后更新,但也没有运气。

经过一些调试,我发现我的数据只有在我重新启动节点服务器后才会更新,这让我想知道我是否在我的设置配置中做错了什么?因为在我的客户端,即使我清除了缓存并进行了硬刷新,或者关闭了我的标签/浏览器,它仍然会抓取我的旧数据。我也没有使用持久链接。

ApolloConfig.js

const cache = new InMemoryCache();

const stateLink = withClientState( cache, resolvers, defaults, typeDefs );

const authLink = setContext((request, prevContext) => (
  headers:  authorization: localStorage.getItem('token') || '' 
));

const httpLink = createHttpLink(
  uri: `$gqlUrlgraphql`
);

const link = ApolloLink.from([stateLink, authLink.concat(httpLink)]);
const client = new ApolloClient(
  link,
  cache,
  connectToDevTools: true
);

服务器端 index.js

app.use(
  '/graphql',
  bodyParser.json(),
  graphqlAuthentication,
  graphqlExpress(
    schema,
    context:  loaders: createLoaders() 
  )
);

app.use('/graphiql', graphiqlExpress( endpointURL: '/graphql' ));

DashboardContainer.js 这里的 refetch 函数只是用来测试我的 props 是否会更新(当函数被调用时,我确实可以在我的服务器端看到正确获取的数据/请求)

class DashboardContainer extends Component 

  refetch = () => 
    this.props.data.refetch();
    this.props.setUser( variables:  user: this.props.data.user  );
  ;

  render() 
    return (
      <div style= display: 'flex', justifyContent: 'center' >
        <Dashboard ...this.props />
        <button onClick=this.refetch>Refresh</button>
      </div>
    );
  


DashboardContainer.propTypes = ;

const query = gql`
  query QUERY_USER_EMAIL($email: String!) 
    user(email: $email) 
      id
      email
      bcomm_client_id
      first_name
      last_name
      __typename
    
  
`;
export default compose(
  graphql(QUERY_GET_USER_EMAIL),
  graphql(query, 
    options: props => (
      variables:  email: props.data.email ,
      fetchPolicy: 'network-only'
    )
  ),
  graphql(MUTATION_UPDATE_USER,  name: 'setUser' )
)(DashboardContainer);

服务器端 Schema.js

 type User 
        id: ID!
        email: String
        last_login: String
        first_name: String
        last_name: String
        roles: String
        is_active: Boolean
        bcomm_client_id: ID
        bcomm_access_token: String
    

    type Query 
        user(email: String!): User!
    

客户端 Queries.js

export const QUERY_GET_USER_EMAIL = gql`
  
    email @client
  
`;

服务器端日志

------- USER -------
TextRow 
  id: 15,
  last_login: null,
  first_name: 'NEK',
  last_name: 'WOW',
  roles: 'Admin',
  is_active: 1,
  email: 'hi@hi.com',
  bcomm_client_id: null,
  bcomm_access_token: null 
-------      Request (POST)     -------
 host: 'localhost:8081',
  connection: 'keep-alive',
  'content-length': '244',
  pragma: 'no-cache',
  'cache-control': 'no-cache',
  accept: '*/*',
  origin: 'https://localhost:3000',
  authorization: '',
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/65.0.3325.181 Safari/537.36',
  'content-type': 'application/json',
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'en-US,en;q=0.9,fr;q=0.8' 
 operationName: 'QUERY_USER_EMAIL',
  variables:  email: 'hi@hi.com' ,
  query: 'query QUERY_USER_EMAIL($email: String!) \n  user(email: $email) \n    id\n    email\n    bcomm_client_id\n    first_name\n    last_name\n    __typename\n  \n\n' 

client.Store console.log

ROOT_QUERY:
email: "hi@hi.com"
user: type: "id", id: "User:15", generated: false
user("email":"hi@hi.com"): type: "id", id: "User:15", generated: false  __typename: "AuthDefaults"
__proto__: Object
User:15:
    bcomm_client_id: null
    email: "hi@hi.com"
    first_name: "KEN"
    id: "15"
    last_name:"WOW"

【问题讨论】:

【参考方案1】:

如果有人仍然想知道这里到底发生了什么。我发现这是由于我用于 GraphQL 的 DataLoaders。我没有正确设置正确的挂钩,因此 DataLoaders 不断向我发送缓存数据,因为它不知道我的数据库中发生的任何更改。这解释了为什么我在重新启动服务器后才看到正确的更新,因为这样做会清空缓存的数据。

【讨论】:

以上是关于如何在 React Apollo 2.0 中正确地重新获取和缓存更新的数据?的主要内容,如果未能解决你的问题,请参考以下文章

react-apollo 中的 data.refetch() 函数如何工作

如何在 react-apollo graphql 查询中正确使用动态变量?

使用 Apollo 和 React 捕获身份验证失败后如何正确重定向

React Apollo:如何在多个地方使用相同查询的结果

设置 AWSAppSyncClient、Apollo 和 React 的正确方法

如何正确地将变量传递给 apollo useQuery?