如何在GraphQL变更操作中通知其他React组件?
解决React Todo App中AddTodo与TodoList的数据同步问题
嘿,针对你描述的这个Todo App组件结构,我刚好有过类似的实践经验,给你几个实用的解决方案,帮你搞定AddTodo添加新事项后TodoList自动更新的问题:
先再明确下你的现有组件逻辑:
组件层级:
<AddTodo />独立于<TodoList>,后者内部包含多个<TodoItem />
TodoList被两个GraphQL HOC包裹:一个用于查询待办事项列表,另一个用于切换事项完成状态,且变更操作会触发TodoList自动重渲染获取最新数据
方案一:利用Apollo Client的refetchQueries(简单直接)
当AddTodo组件调用添加待办的mutation时,直接指定让TodoList的查询重新执行,这样就能拉取最新的列表数据:
// AddTodo组件中的mutation配置 import { gql, graphql } from 'react-apollo'; const ADD_TODO = gql` mutation AddTodo($text: String!) { addTodo(text: $text) { id text completed } } `; // 假设TodoList用到的查询是GET_TODOS const GET_TODOS = gql` query GetTodos { todos { id text completed } } `; export default graphql(ADD_TODO, { props: ({ mutate }) => ({ addTodo: (text) => mutate({ variables: { text }, // 触发TodoList的查询重新获取数据 refetchQueries: [{ query: GET_TODOS }] }) }) })(AddTodo);
方案二:手动更新Apollo缓存(性能更优)
如果不想每次添加都重新请求服务器,可以直接操作Apollo的缓存,把新添加的todo插入到现有列表中,这样TodoList会因为缓存更新自动重渲染:
// AddTodo组件的mutation配置 addTodo: (text) => mutate({ variables: { text }, update: (cache, { data: { addTodo } }) => { // 先读取缓存中已有的todos数据 const existingTodos = cache.readQuery({ query: GET_TODOS }); // 将新todo插入到缓存列表中 cache.writeQuery({ query: GET_TODOS, data: { todos: [...existingTodos.todos, addTodo] } }); } })
方案三:调整组件结构(适合简单场景)
如果你的布局允许,可以把AddTodo组件放到TodoList内部,这样当TodoList因为mutation更新时,AddTodo也能自然同步状态,不过这种方式灵活性稍差,只适合结构简单的场景:
// TodoList组件内部 const TodoList = ({ todos, toggleTodo }) => ( <div className="todo-container"> <AddTodo /> {/* 将AddTodo作为TodoList的子组件 */} <div className="todo-list"> {todos.map(todo => ( <TodoItem key={todo.id} todo={todo} toggleTodo={toggleTodo} /> ))} </div> </div> ); // 然后用GraphQL HOC包裹TodoList export default graphql(GET_TODOS)(graphql(TOGGLE_TODO)(TodoList));
方案四:使用GraphQL订阅(实时同步场景)
如果你的GraphQL服务器支持订阅功能,还可以给TodoList添加订阅监听,这样不管哪个组件添加了新todo,TodoList都能实时收到更新:
// TodoList组件的HOC配置 graphql(GET_TODOS, { props: ({ data: { todos, subscribeToMore } }) => ({ todos, subscribeToNewTodos: () => subscribeToMore({ document: gql` subscription NewTodo { newTodo { id text completed } } `, updateQuery: (prevData, { subscriptionData }) => { if (!subscriptionData.data) return prevData; const newTodo = subscriptionData.data.newTodo; // 避免重复添加 if (!prevData.todos.find(todo => todo.id === newTodo.id)) { return { todos: [...prevData.todos, newTodo] }; } return prevData; } }) }) })(TodoList); // 在TodoList的生命周期中启动订阅 componentDidMount() { this.props.subscribeToNewTodos(); }
总结
如果只是简单的同步需求,优先选方案一或方案二;如果需要跨组件甚至跨客户端的实时同步,就用方案四;组件结构调整的方案只适合布局允许的简单场景。
内容的提问来源于stack exchange,提问作者dark_ruby
相关产品推荐
相关产品推荐

