React Apollo - 孩子没有根据结果变化进行更新

Posted

技术标签:

【中文标题】React Apollo - 孩子没有根据结果变化进行更新【英文标题】:React Apollo - Child not updating based on result change 【发布时间】:2019-10-11 04:48:51 【问题描述】:

我正在使用 React Apollo 来处理 GaphQL 后端。我正在创建的功能允许用户编辑另一个用户的管理员权限。

我有两个组件。一个组件包含 React Apollo 逻辑,包括查询和突变组件。当这个组件被渲染时,从路由器中的 match 参数向下传递的 prop 指定在 GraphQL 查询中使用哪个管理员来获取其权限。当检索到管理员权限组件所需的数据时,它将作为道具传递给子组件,该子组件根据该道具设置初始状态。子组件不包含 React Apollo 逻辑,仅包含复选框和其他输入,这些输入会在组件状态发生更改时更新它们。单击保存更改按钮时,将调用父级的变异函数,传递新更新的权限以将变量插入到变异查询中。

我面临的问题是,有时状态不会根据道具而改变。例如,访问管理员 1,然后路由器会在单击按钮以访问管理员 2 时更新 URL。它一直工作到此为止。然后,当单击按钮访问 admin 1 时,路由器会再次更新 URL,尽管 props 已使用新值更新,但 admin 2 仍显示在子组件中。注意子组件的构造函数不会被再次调用。

关于如何确保 GraphQL 查询生成的数据显示正确的子组件实例有什么建议吗?此外,关于如何改进此组件的结构的任何建议。

render()
if(this.props.steamID ===  null) return null;

const QUERY_VARIABLES = 
  serverID: this.props.serverID,
  selectedAdminSteamID: this.props.steamID,
  currentAdminSteamID: Auth.claim.steamID
;

return (
  <Query
    query=QUERY
    variables=QUERY_VARIABLES
  >
    ( loading, error, data ) => 
      if (loading) return ( ... );
      if (error) return ( ... );
      if(!data.server.selectedAdmin) return ( ... );

      return (
        <Mutation
          mutation=MUTATION
          update=(cache,  data:  updateAdminPermission  ) => 
            const data = cache.readQuery( query: QUERY, variables: QUERY_VARIABLES );
            data.server.selectedAdmin = updateAdminPermission;
            cache.writeQuery( query: QUERY, variables: QUERY_VARIABLES, data: data, );
          
        >

          (updateAdminPermission,  loading, error ) => (
            <>
              <AdminPermissions
                serverID=this.props.serverID
                steamID=this.props.steamID

                selectedAdmin=data.server.selectedAdmin
                currentAdmin=data.server.currentAdmin

                updatePermissionFunction=(variables) => 
                  updateAdminPermission( variables )
                
                updatePermissionLoading=loading
              />
              <GraphQLErrorModal error=error />
            </>
          )

        </Mutation>
      );
    
  </Query>
);


class AdminPermissions extends React.Component
    constructor(props)
      super();

      this.state = props.selectedAdmin;

      this.guid = React.createRef();
      this.updatePermission = this.updatePermission.bind(this);
      this.saveChanges = this.saveChanges.bind(this);
    

    updatePermission(changedPermission, value)
      if(changedPermission === 'manageAssignPermissions' && value > 0)
        for(let permission of panelPermissions.concat(gamePermissions))
          if(permission.permission === 'manageAssignPermissions') continue;
          this.setState( [permission.permission]: 2 );
        
      
      this.setState( [changedPermission]: value );
    

    saveChanges()
      this.props.updatePermissionFunction(
        serverID: this.props.serverID,
        steamID: this.props.steamID,
        guid: this.guid.current.value,
        ...this.state
      );
    

    render()
        // renders pairs of checkboxes with checked value based on state and on change event that calls update permissions method passing the name of the associated permission and a value that is calculated based on which boxes in the pair are ticked.
    
 

查询(更新)

  query AdminPermission($serverID: Int!, $selectedAdminSteamID: String!, $currentAdminSteamID: String!) 
    server(id: $serverID) 
      id

      selectedAdmin: adminPermission(steamID: $selectedAdminSteamID)     
        _id

        admin 
          _id

          steamID
          displayName
          avatar
        

        player 
          _id
          guid
        

        manageAssignPermissions
        viewAdminPermissions
        ...
      
      currentAdmin: adminPermission(steamID: $currentAdminSteamID) 
        _id

        admin 
          _id
          steamID
        

        manageAssignPermissions
        viewAdminPermissions
        ...
      
    
  

【问题讨论】:

您发送的实际查询是什么? 查询的内容并不真正相关,但它基本上会获取一组属性,即 0、1 或 2 以及有关管理员的一些附加属性,例如他们的显示名称、头像等。 尝试将fetchPolicy='cache-and-network' 添加到您的&lt;Query&gt; @ThomasSmyth 如果查询的内容导致您的查询未正确缓存,则它可能是相关的。大多数情况下,这类问题是由于 Apollo 的缓存规范化造成的。 @DanielRearden 我很抱歉,我没有意识到这个问题是由于缓存造成的。 【参考方案1】:

您必须将QueryfetchPolicy设置为cache-and-network,默认为cache-first

<Query
    query=QUERY
    variables=QUERY_VARIABLES
    fetchPolicy='cache-and-network'
  >

如果您阅读documentation:

cache-first:这是我们总是尝试读取的默认值 首先从缓存中获取数据。如果满足您所需的所有数据 查询在缓存中,然后将返回该数据。阿波罗将 仅当缓存结果不可用时才从网络获取。这 fetch 策略旨在最小化发送的网络请求数量 渲染你的组件。

cache-and-network:此获取策略将 让 Apollo 首先尝试从缓存中读取数据。如果所有数据 完成查询所需的数据在缓存中,那么该数据将是 回来。但是,无论是否完整的数据在 您的缓存此 fetchPolicy 将始终使用网络执行查询 接口不像缓存优先,它只会执行你的查询,如果 查询数据不在您的缓存中。此获取策略针对用户进行了优化 在尝试保留缓存数据的同时获得快速响应 以额外网络为代价与您的服务器数据一致 请求。

【讨论】:

更改查询的变量最初应该会导致缓存未命中,即使策略是 cache-first。如果从缓存中返回了错误的数据,通常是因为 dataIdFromObject 未正确实现或缺少 id 字段。虽然这可能会解决问题,但这也意味着每当组件重新呈现时,都会向服务器发出请求,这可能是不可取的。 @DanielRearden 进行缓存是在结果数据中返回数据库中对象的 id 字段的常见做法吗?如果是这样,我认为这需要包含在服务器上的 typedef 中并由查询明确请求?谢谢你们的帮助。 是的,每个对象都应该返回一个 id 或 _id 字段,因为 Apollo 使用它来生成缓存键。如果一个不可用,您可以使用 dataIdFromObject。见here。 @ThomasSmyth 是的,重要的是在查询和突变的结果中包含 id 以让 apollo 自动更新缓存more info 只有在添加新项目时才需要cache.writeQuery,在更新的情况下,如果您在突变结果中包含 id,缓存将自动更新

以上是关于React Apollo - 孩子没有根据结果变化进行更新的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Apollo Query 构造 React 代码,以便随机查询的对象数组不会随着状态变化而重新呈现?

React Apollo:从组件状态动态更新 GraphQL 查询

React Apollo GraphQL 搜索/过滤器

Apollo:在订阅更新时更新 React 道具?

React - Apollo 客户端,如何将查询结果添加到状态

将 ID 从 React 传递给 Apollo 以找到正确的结果?