Я новичок в 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
, если я определяю ее каталог импорта как @/
, верно? Так что же может быть причиной этой проблемы? Есть ли конфигурация, которую я забыл добавить? Если кто-нибудь знает или имеет представление об этом, пожалуйста, дайте мне знать.
@AudunHilden, пожалуйста, дайте мне знать, если это работает для вас codeandbox.io/p/github/gfcf14/greendream-2024/main
Хуки React необходимо использовать внутри клиентских компонентов. Преобразование компонента Nav в клиентский компонент с помощью директивы "use client"
вверху src/components/NavBar/index.tsx
сработало для меня:
Вау, это было? Спасибо!
Не беспокойся! Если исправление сработает, не могли бы вы отметить мой ответ как принятый?
да, я только что сделал. Еще раз спасибо!
Можете ли вы создать воспроизводимый образец на codeandbox?