React中useCallback传递当前数据失效问题的解决方案(Next.js)
问题描述
我用Next.js和React开发应用,尝试通过函数打开模态框并传递当前状态的数据。已成功从Supabase数据库获取更新后的数据,在useEffect中打印该数据并将其存入ref,但触发函数时,打印的rolesAndAllowedRef.current是旧值,传递给函数的参数已过时。我知道问题可能源于函数定义,但不确定如何修复以传递rolesAndAllowedRef.current中的当前数据,尝试过useCallback但未解决,求修复方法。
相关代码
"use client"; import { Edit, Trash } from "lucide-react"; import { useParams, useRouter } from "next/navigation"; import { cn } from "@/lib/utils"; import { ModalType, useModal } from "../../../hooks/use-modal-store"; import { ActionTooltip } from "../util/ActionTooltip"; import { iconMap, iconMapSidebar } from "./space/data"; import { useCallback, useEffect, useRef, useState } from "react"; import { useRoleUpdateEffect } from "../modals/hooks/useRoleUpdateEffect"; import { createClientComponentClient } from "@supabase/auth-helpers-nextjs"; interface CollectiveSpaceProps { spaces: Space[]; space: Space; collective: Collective; userRole: Role; roles: Role[]; } export const CollectiveSpace = ({ spaces, space, collective, userRole, roles, }: CollectiveSpaceProps) => { const { onOpen } = useModal(); const params = useParams(); const router = useRouter(); const supabase = createClientComponentClient(); const onClick = () => { router.push(`/collective/${params?.unique}/${space.slug}`); }; const [rolesAndAllowed, setRolesAndAllowed] = useState<RoleAndAllowed[]>( roles.map((role) => ({ ...role, allowed: space.allowed.includes(role.id) })) ); const rolesAndAllowedRef = useRef(rolesAndAllowed); const onAction = useCallback( (e: React.MouseEvent, action: ModalType) => { console.log(rolesAndAllowedRef.current); e.stopPropagation(); onOpen(action, { space, collective, spaces, rolesAndAllowed: rolesAndAllowedRef.current, }); }, [onOpen, space, collective, spaces, rolesAndAllowedRef] ); useEffect(() => { rolesAndAllowedRef.current = rolesAndAllowed; console.log(rolesAndAllowedRef.current); }, [rolesAndAllowed, onOpen, space, collective, spaces]); useRoleUpdateEffect( supabase, collective, rolesAndAllowed, setRolesAndAllowed ); /* so error is that I can't pass the updated roles into the edit space modal and then the create space modal meaning that the roles being eidted are outdated */ return ( <button onClick={onClick} className={cn( "group px-2 py-2 rounded-md flex items-center gap-x-2 w-full hover:bg-zinc-700/10 dark:hover:bg-zinc-700/50 transition mb-1", params?.spaceId === space.id && "bg-zinc-700/20 dark:bg-zinc-700" )} > {iconMapSidebar[space?.type as keyof typeof iconMap]} <p className={cn( "line-clamp-1 font-semibold text-sm text-zinc-500 group-hover:text-zinc-600 dark:text-zinc-400 dark:group-hover:text-zinc-300 transition", params?.spaceId === space.id && "text-primary dark:text-zinc-200 dark:group-hover:text-white" )} > {<> {space.name} </>} </p> {space.name !== "general" && userRole.canCreate && ( <div className="flex items-center ml-auto gap-x-2"> <ActionTooltip label="Edit"> <Edit onClick={(e) => onAction(e, "editSpace")} className="hidden w-4 h-4 transition group-hover:block text-zinc-500 hover:text-zinc-600 dark:text-zinc-400 dark:hover:text-zinc-300" /> </ActionTooltip> <ActionTooltip label="Delete"> <Trash onClick={(e) => onAction(e, "deleteSpace")} className="hidden w-4 h-4 transition group-hover:block text-zinc-500 hover:text-zinc-600 dark:text-zinc-400 dark:hover:text-zinc-300" /> </ActionTooltip> </div> )} </button> ); };
修复方法
方案一:直接使用状态值(推荐)
不需要借助useRef,直接将rolesAndAllowed状态传入onAction回调,并把它加入useCallback的依赖数组。这样每当状态更新时,useCallback会重新生成回调,确保拿到最新的状态值:
const onAction = useCallback( (e: React.MouseEvent, action: ModalType) => { e.stopPropagation(); onOpen(action, { space, collective, spaces, rolesAndAllowed, // 直接使用状态值 }); }, [onOpen, space, collective, spaces, rolesAndAllowed] // 添加rolesAndAllowed到依赖 ); // 可以删除rolesAndAllowedRef和对应的useEffect
方案二:修正Ref的使用方式(如果必须保留Ref)
如果一定要用useRef,注意useCallback的依赖里不需要包含rolesAndAllowedRef(因为Ref对象本身是稳定不变的,加入依赖不会触发回调更新),同时确保useEffect只在rolesAndAllowed变化时更新Ref:
const rolesAndAllowedRef = useRef(rolesAndAllowed); const onAction = useCallback( (e: React.MouseEvent, action: ModalType) => { console.log(rolesAndAllowedRef.current); e.stopPropagation(); onOpen(action, { space, collective, spaces, rolesAndAllowed: rolesAndAllowedRef.current, }); }, [onOpen, space, collective, spaces] // 移除rolesAndAllowedRef依赖 ); useEffect(() => { rolesAndAllowedRef.current = rolesAndAllowed; }, [rolesAndAllowed]); // 只监听rolesAndAllowed的变化,移除其他无关依赖
问题根源说明
- 你把
rolesAndAllowedRef加入了useCallback的依赖数组,但Ref对象本身是稳定不变的,所以useCallback不会因为Ref的current属性更新而重新生成回调。 - 你的
useEffect依赖了太多无关变量(onOpen、space等),可能导致Ref更新时机异常,无法及时同步最新的状态值。
内容的提问来源于stack exchange,提问作者charlietlamb
相关产品推荐
相关产品推荐

