如何在普通业务函数中正确调用Apollo Client的useMutation Hook?
解决方案
核心问题本质
你违反了React Hook的调用规则:Hook只能在函数组件或者自定义Hook的顶层作用域调用,不能在普通业务函数、异步回调内部调用。你原先的createNotification是普通函数,内部调用了useMutation这个Hook,在addMovie的异步回调中执行时自然会触发报错。
代码修改步骤
第一步:改造通知创建逻辑为自定义Hook
将CreateNotification.tsx中的普通函数改为符合React规范的自定义Hook,不要直接执行mutation,而是返回触发mutation的可调用函数:
// CreateNotification.tsx import { useMutation } from '@apollo/client'; import resolvers from '../resolvers'; // 自定义Hook命名必须以use开头 const useCreateNotification = () => { // 在Hook顶层调用useMutation,拿到触发函数 const [triggerCreate] = useMutation(resolvers.mutations.CreateNotification); // 返回实际执行业务逻辑的函数,供外部按需调用 return (notification: { movie: IMovie, user: any }) => { triggerCreate({ variables: { movie_id: notification.movie.id, actor_id: notification.user.id, message: `${notification.user.user_name} 已将 ${notification.movie.original_title} 添加到观影列表`, }, }); }; }; export default useCreateNotification;
第二步:调整组件内调用逻辑
在AddMovie.tsx组件顶层调用自定义Hook拿到触发函数,再在异步请求完成的回调中调用该函数即可:
// AddMovie.tsx import useCreateNotification from './CreateNotification'; const AddMovieToWatchList = ({movie}: {movie: IMovie}) => { // 组件顶层调用自定义Hook,拿到通知触发函数 const createNotification = useCreateNotification(); const [addUserToMovie] = useMutation(resolvers.mutations.AddUserToMovie); const addMovie = async (movie: IMovie) => { await addUserToMovie({ variables: {...movie, tmdb_id: movie.id}, update: (cache, {data}) => { cache.modify({ fields: { moviesFromUser: () => { return [...data.addUserToMovie]; }, }, }); }, }).then(() => { // 异步请求完成后按需调用通知触发逻辑 createNotification({movie: movie, user: currentUserVar()}); }); }; return ( <a className={classes.addMovie} onClick={() => addMovie(movie)}> 添加电影到我的观影列表 </a> ); }
实现原理
自定义Hook内部的useMutation是在组件渲染的顶层阶段执行的,完全符合React Hook的调用规则。返回的触发函数只是普通的业务执行函数,可以在任意时机(比如异步回调、点击事件回调)调用,不会触发Hook规则报错,同时也不会在组件渲染时自动执行,只有你主动调用时才会发起创建通知的请求。
内容的提问来源于stack exchange,提问作者Peter Boomsma
相关产品推荐
相关产品推荐

