You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用apollo-link-state观测Apollo Client中的派生状态?

其实你说的这个痛点我太懂了——手动在每个Todo增删改操作里同步更新totalTodos不仅繁琐,还特别容易遗漏出错。好在Apollo Link State本身就支持通过本地解析器动态计算派生状态,完全不用把totalTodos这类计算值存在缓存里,而且它是可观测的,只要缓存里的Todo列表一变,查询结果会自动更新,完美解决你的问题!

核心思路很简单:在本地Schema里定义派生字段(比如totalTodos),然后在解析器函数中直接从缓存读取原始的Todo列表,动态计算出需要的值。这样所有增删改操作只需要维护原始的todos缓存,派生字段会自动跟随原始数据变化。

下面给你一个简化的完整示例:

1. 定义本地Schema

首先在本地Schema中声明totalTodos这个派生字段,和todos放在同一个Query类型下:

import { gql } from '@apollo/client';

const typeDefs = gql`
  type Todo {
    id: ID!
    text: String!
    completed: Boolean!
  }

  type Query {
    todos: [Todo]!
    totalTodos: Int! # 派生字段,不需要存在缓存中
  }

  type Mutation {
    addTodo(text: String!): Todo!
    toggleTodo(id: ID!): Todo!
    deleteTodo(id: ID!): Boolean!
  }
`;

2. 配置解析器(重点看totalTodos的实现)

在解析器中,totalTodos的逻辑就是从缓存读取todos列表,返回它的长度。而所有Mutation操作只需要更新todos缓存即可,完全不用管totalTodos:

const resolvers = {
  Query: {
    // 读取缓存中的Todo列表
    todos: (_root, _args, { cache }) => {
      const data = cache.readQuery({ query: GET_TODOS });
      return data.todos || [];
    },
    // 动态计算Todo总数,完全不需要手动维护
    totalTodos: (_root, _args, { cache }) => {
      const data = cache.readQuery({ query: GET_TODOS });
      return (data.todos || []).length;
    },
  },
  Mutation: {
    addTodo: (_root, { text }, { cache }) => {
      const data = cache.readQuery({ query: GET_TODOS });
      const newTodo = {
        id: Date.now().toString(),
        text,
        completed: false,
        __typename: 'Todo',
      };
      // 只更新todos缓存
      cache.writeQuery({
        query: GET_TODOS,
        data: { todos: [...data.todos, newTodo] },
      });
      return newTodo;
    },
    toggleTodo: (_root, { id }, { cache }) => {
      const todoFragment = gql`
        fragment TodoFragment on Todo {
          id
          completed
        }
      `;
      const todo = cache.readFragment({
        id: `Todo:${id}`,
        fragment: todoFragment,
      });
      const updatedTodo = {
        ...todo,
        completed: !todo.completed,
        __typename: 'Todo',
      };
      // 更新单个Todo的缓存,todos列表会自动同步
      cache.writeFragment({
        id: `Todo:${id}`,
        fragment: todoFragment,
        data: updatedTodo,
      });
      return updatedTodo;
    },
    deleteTodo: (_root, { id }, { cache }) => {
      const data = cache.readQuery({ query: GET_TODOS });
      const newTodos = data.todos.filter(todo => todo.id !== id);
      // 只更新todos缓存
      cache.writeQuery({
        query: GET_TODOS,
        data: { todos: newTodos },
      });
      return true;
    },
  },
};

// 定义读取Todo列表的基础查询
const GET_TODOS = gql`
  query GetTodos {
    todos {
      id
      text
      completed
    }
  }
`;

3. 在组件中使用派生字段

现在你可以直接在组件中查询totalTodos,它会自动响应缓存中todos的变化:

import { useQuery } from '@apollo/client';

const TodoStats = () => {
  const { data, loading } = useQuery(gql`
    query TodoStats {
      totalTodos
    }
  `);

  if (loading) return <div>Loading...</div>;

  return <div>Total Todos: {data.totalTodos}</div>;
};

扩展:更复杂的派生状态

如果需要计算已完成的Todo数量这类更复杂的派生值,逻辑也是一样的,只需要在解析器中添加过滤逻辑:

// 在Query解析器中添加
completedTodos: (_root, _args, { cache }) => {
  const data = cache.readQuery({ query: GET_TODOS });
  return (data.todos || []).filter(todo => todo.completed).length;
}

这种方式的优势非常明显:

  • 不需要维护冗余的派生状态缓存,所有逻辑集中在解析器里
  • 天然响应式:缓存中原始数据变化时,派生字段会自动重新计算并更新组件
  • 减少出错概率,不用在每个Mutation里重复写更新派生状态的代码

内容的提问来源于stack exchange,提问作者tgk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:11:42