如何封装自定义React Hook实现Apollo Client查询与Mutation逻辑复用
基于Apollo Client的可复用CRUD自定义Hook实现
React Hook只能在函数组件或其他自定义Hook内部调用,将公共CRUD逻辑封装为自定义Hook,可变配置通过入参传入,即可实现全页面复用。
步骤1:封装通用CRUD自定义Hook
import { useQuery, useMutation, useMemo } from '@apollo/client' import { notification } from 'antd' // 替换为你项目使用的提示组件即可 /** * 通用Apollo CRUD操作Hook * @param {Object} config 配置项 * @param {DocumentNode} config.searchQuery 列表查询GQL语句 * @param {DocumentNode} config.createMutation 新增GQL语句 * @param {DocumentNode} [config.updateMutation] 更新GQL语句(可选) * @param {DocumentNode} [config.deleteMutation] 删除GQL语句(可选) * @param {Object} config.variables 查询/突变默认携带的公共变量(如profileId) * @param {Object} [config.tips] 各操作成功提示文案(可选,可覆盖默认值) */ const useCrudOperations = (config) => { const { searchQuery, createMutation, updateMutation, deleteMutation, variables = {}, tips = { addSuccess: '新增成功', updateSuccess: '更新成功', deleteSuccess: '删除成功' } } = config // 列表查询逻辑 const { data: listData, loading: queryLoading, error: queryError, refetch: refetchList } = useQuery(searchQuery, { variables }) // 新增突变逻辑 const [addRow, { loading: addLoading, error: addError }] = useMutation(createMutation, { refetchQueries: [{ query: searchQuery, variables }], onCompleted: () => { notification.success({ message: '操作成功', description: tips.addSuccess }) } }) // 更新突变逻辑(可选) const [updateRow, { loading: updateLoading, error: updateError }] = updateMutation ? useMutation(updateMutation, { refetchQueries: [{ query: searchQuery, variables }], onCompleted: () => { notification.success({ message: '操作成功', description: tips.updateSuccess }) } }) : [() => {}, { loading: false, error: null }] // 删除突变逻辑(可选) const [deleteRow, { loading: deleteLoading, error: deleteError }] = deleteMutation ? useMutation(deleteMutation, { refetchQueries: [{ query: searchQuery, variables }], onCompleted: () => { notification.success({ message: '操作成功', description: tips.deleteSuccess }) } }) : [() => {}, { loading: false, error: null }] // 合并所有loading状态,替代页面单独写useEffect const globalLoading = useMemo(() => { return queryLoading || addLoading || updateLoading || deleteLoading }, [queryLoading, addLoading, updateLoading, deleteLoading]) // 暴露给页面的返回值 return { // 数据 listData, queryError, addError, updateError, deleteError, globalLoading, // 方法 addRow, updateRow, deleteRow, refetchList } } export default useCrudOperations
步骤2:页面中直接调用使用
不需要再写重复的查询、突变、loading合并逻辑,只需要传入对应GQL和变量即可:
// 页面组件内部 import useCrudOperations from '@/hooks/useCrudOperations' import { SEARCH_ALL_ITEMS, CREATE_ITEMS, UPDATE_ITEMS, DELETE_ITEMS } from './gql' const DemoPage = () => { const profileId = 'xxx' // 你的业务变量 // 直接调用Hook获取所有需要的能力 const { listData, globalLoading, addRow, updateRow, deleteRow } = useCrudOperations({ searchQuery: SEARCH_ALL_ITEMS, createMutation: CREATE_ITEMS, updateMutation: UPDATE_ITEMS, deleteMutation: DELETE_ITEMS, variables: { profileId }, tips: { addSuccess: '信息新增成功' } }) // 页面内直接调用addRow等方法即可,不用再单独处理refetch、提示、loading const handleAdd = (formData) => { addRow({ variables: formData }) } return <div> {/* 你的业务渲染逻辑 */} </div> }
可扩展说明
- 如果需要自定义
onCompleted/onError逻辑,可以在Hook的入参里增加对应回调参数,优先级高于默认逻辑即可 - 如果有懒查询需求,可以在Hook中增加
useLazyQuery的封装逻辑,和上述思路一致 - 所有内置的默认配置都支持通过入参覆盖,适配不同页面的个性化需求
内容的提问来源于stack exchange,提问作者WildThing
相关产品推荐
相关产品推荐

