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

Next.js 13+TypeScript项目useDrag触发useMemo读取null属性错误求助

问题描述

在基于TypeScript的Next.js 13项目中使用useDrag钩子时,Visual Studio Code未检测到错误,但页面渲染时出现服务器错误:

Server Error
TypeError: Cannot read properties of null (reading 'useMemo')
This error happened while generating the page. Any console logs will be displayed in the terminal window.

相关源码如下:

import { useDrag } from 'react-dnd';
import { ItemType } from './../types/ItemType';
import React, { useMemo } from 'react';

interface HeaderProps {
  width: number;
  height: number;
  background: string;
}

const Header = ({ width, height, background }: HeaderProps) => {
  const [drag, dragRef] = useMemo(() => {
    return useDrag(() => ({
      type: ItemType.HEADER,
      item: { width, height, background },
      collect: (monitor) => ({
        isDragging: !!monitor.isDragging(),
      }),
    }), [width, height, background]);
  }, [width, height, background]);

  const isDragging = drag?.isDragging as boolean;

  return (
    <div
      ref={dragRef}
      style={{
        width,
        height,
        background,
        opacity: isDragging ? 0.5 : 1,
        cursor: 'move',
      }}
    />
  );
};

export const SideMenu = () => {
  return (
    <div style={{ width: '300px', background: '#f8f8f8' }}>
      <Header width={200} height={50} background="blue" />
    </div>
  );
};
问题原因与解决方案

问题根源

错误核心是在useMemo的回调函数内部调用了useDrag钩子,违反了React的Hook规则:所有Hook必须在组件顶层作用域或自定义Hook的顶层作用域中调用,不能嵌套在useMemo、useEffect等Hook的回调函数里。这种嵌套会导致React无法正确追踪Hook调用顺序,进而抛出异常。

另外,useDrag本身已内部实现依赖项缓存逻辑,不需要额外用useMemo包裹。

修正后的代码

直接移除useMemo,将依赖项直接传给useDrag的第二个参数即可:

import { useDrag } from 'react-dnd';
import { ItemType } from './../types/ItemType';
import React from 'react';

interface HeaderProps {
  width: number;
  height: number;
  background: string;
}

const Header = ({ width, height, background }: HeaderProps) => {
  const [drag, dragRef] = useDrag(() => ({
    type: ItemType.HEADER,
    item: { width, height, background },
    collect: (monitor) => ({
      isDragging: !!monitor.isDragging(),
    }),
  }), [width, height, background]);

  const isDragging = drag?.isDragging as boolean;

  return (
    <div
      ref={dragRef}
      style={{
        width,
        height,
        background,
        opacity: isDragging ? 0.5 : 1,
        cursor: 'move',
      }}
    />
  );
};

export const SideMenu = () => {
  return (
    <div style={{ width: '300px', background: '#f8f8f8' }}>
      <Header width={200} height={50} background="blue" />
    </div>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 11:23:28