Ошибка NextJS «не является функцией» при попытке импортировать собственный хук

Я новичок в NextJS и пытаюсь создать веб-сайт, для которого я начал с компонента панели навигации, и я хочу убедиться, что он реагирует на размеры области просмотра. В настоящее время у меня есть такая структура папок:

Где я прикрепляю контекст src/context/ViewportContext.tsx к основному макету src/app/layout.tsx следующим образом:

import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import { ViewportProvider } from '@/context/ViewportContext';

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang='en'>
      <body className = {inter.className}>
        <ViewportProvider>
          {children}
        </ViewportProvider>
      </body>
    </html>
  );
}

Файл ViewportContext.tsx выглядит так:

'use client';

import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';

type ViewportContextType = {
  width: number;
  height: number;
};

const ViewportContext = createContext<ViewportContextType | undefined>(undefined);

export const ViewportProvider = ({ children }: { children: ReactNode }) => {
  const [size, setSize] = useState<ViewportContextType>({
    width: 0,  // Initialize with 0, because the actual value will be set on the client
    height: 0,
  });

  useEffect(() => {
    if (typeof window !== 'undefined') {
      const handleResize = () => {
        setSize({
          width: window.innerWidth,
          height: window.innerHeight,
        });
      };

      // Sets initial size
      handleResize();

      window.addEventListener('resize', handleResize);
      return () => window.removeEventListener('resize', handleResize);
    }
  }, []);

  return (
    <ViewportContext.Provider value = {size}>
      {children}
    </ViewportContext.Provider>
  );
}

export const useViewport = () => {
  const context = useContext(ViewportContext);
  if (!context) {
    throw new Error('useViewport must be used within a ViewportProvider');
  }
  return context;
}

как вы видите, у него также есть крючок useViewport, который я планирую использовать для получения размеров окна просмотра и работы с ними в другом месте. Мой файл главной страницы по адресу src/app/page.tsx сейчас выглядит так:

import NavBar from '@/components/NavBar';

export default function Home() {
  return (
    <>
      <NavBar />
    </>
  );
}

и компонент NavBar, который я указываю и который находится в src/components/NavBar/index.tsx, выглядит так:

import Image from 'next/image';
import styles from './NavBar.module.css';
import useDeviceType from '@/utils/useDeviceType';

const NavBar: React.FC = () => {
  const { isTabletOrSmaller, isDesktopOrLarger } = useDeviceType();

  console.info(isTabletOrSmaller);

  return (
    <div className = {styles.container}>
      <Image
        alt='GreenDream logo'
        src='/images/logo.svg'
        width = {162}
        height = {46}
      />
      <Image
        alt='Sandwhich'
        className = {styles.sandwhich}
        src='/images/sandwhich.svg'
        width = {36}
        height = {32}
      />
    </div>
  )
}

export default NavBar;

Сейчас я пытаюсь протестировать работоспособность хука, useDeviceType который находится по адресу src/utils/useDeviceType.tsx:

import { useViewport } from '@/context/ViewportContext';

const useDeviceType = () => {
  const { width } = useViewport();

  const isMobile = width <= 768;
  const isTablet = width > 768 && width <= 1024;
  const isDesktop = width > 1024 && width <= 1440;
  const isLargeDesktop = width > 1440;
  const isTabletOrSmaller = width <= 1024;
  const isDesktopOrLarger = width > 1024;

  return { 
    isMobile,
    isTablet,
    isDesktop,
    isLargeDesktop,
    isTabletOrSmaller,
    isDesktopOrLarger,
  };
}

export default useDeviceType;

Если все работает правильно, он должен просто напечатать true, если мое окно просмотра меньше 1024 пикселей, но при запуске я получаю эту ошибку useViewport is not a function:

Я не совсем понимаю, почему кажется, что импорт не работает для крючка useViewport, поскольку другая функция в этом файле правильно импортируется в layout.tsx. Кроме того, мой tsconfig.json сейчас выглядит так:

{
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

Насколько я понимаю, строка "@/*": ["./src/*"] должна позволять импортировать что-либо из папки src, если я определяю ее каталог импорта как @/, верно? Так что же может быть причиной этой проблемы? Есть ли конфигурация, которую я забыл добавить? Если кто-нибудь знает или имеет представление об этом, пожалуйста, дайте мне знать.

Можете ли вы создать воспроизводимый образец на codeandbox?

Audun Hilden 21.08.2024 20:18

@AudunHilden, пожалуйста, дайте мне знать, если это работает для вас codeandbox.io/p/github/gfcf14/greendream-2024/main

gfcf14 21.08.2024 21:31
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
2
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Хуки React необходимо использовать внутри клиентских компонентов. Преобразование компонента Nav в клиентский компонент с помощью директивы "use client" вверху src/components/NavBar/index.tsx сработало для меня:

Вау, это было? Спасибо!

gfcf14 21.08.2024 23:46

Не беспокойся! Если исправление сработает, не могли бы вы отметить мой ответ как принятый?

Michael Beck 22.08.2024 00:10

да, я только что сделал. Еще раз спасибо!

gfcf14 22.08.2024 01:18

Другие вопросы по теме