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

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的变化,移除其他无关依赖

问题根源说明

  1. 你把rolesAndAllowedRef加入了useCallback的依赖数组,但Ref对象本身是稳定不变的,所以useCallback不会因为Ref的current属性更新而重新生成回调。
  2. 你的useEffect依赖了太多无关变量(onOpen、space等),可能导致Ref更新时机异常,无法及时同步最新的状态值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:11:03