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
相关产品推荐
相关产品推荐

