Next.js как передать ‘setSomeState’ дочерним компонентам

Вопрос или проблема

Next.js как передать ‘setSomeState’ дочерним компонентам

Я пытаюсь создать приложение с блоком чата, который не обновляется при смене страницы. В дочерних страницах содержатся подсказки, чтобы избавить пользователя от необходимости вводить общие ответы. Все мои страницы находятся в директории /board, где есть страница верхнего уровня под названием ‘BoardContainer.tsx’:

'use client'
import React, { ReactElement, ReactNode } from 'react';
import { useState } from 'react';
import { usePathname } from 'next/navigation';
import ChatBlock from '../components/ChatBlock/ChatBlock';
import AboutPage from '../dashboard/about/background/page';

interface BoardContainerProps {
  children: ReactNode;
}

const BoardContainer: React.FC<BoardContainerProps> = ({ children }) => {
  const [selectedPrompt, setSelectedPrompt] = useState('');
  const pathname = usePathname();

  const sendPrompt = (prompt: string) => {
    setSelectedPrompt(prompt);
  }

  const childrenWithProps = React.Children.map(children, child => {
    console.dir(child);
    if (React.isValidElement(child)) {
      return React.cloneElement(child as ReactElement<any>, { sendPrompt });
    }
    console.dir(child);
    return child;
  });

  console.dir(pathname);

  return (
    <>
      <>
        { pathname === '/board/about' ?  <AboutPage sendPrompt={sendPrompt} /> : <PageContent /> }
        {/* Это работает, но не подходит для более чем 2 дочерних страниц */}

        {/* childrenWithProps */}
        {/* это тоже не работает */}

        {/* children */}
      <>

      <>
        <ChatBlock
          prompt={selectedPrompt}
        />
      </>
    </>
  )
}

export default BoardContainer;

Я нашел обходной путь, используя usePathname и фактически жестко кодируя значение дочернего элемента и передавая функцию sendPrompt, но это громоздко. Я пробовал использовать React.cloneElement, но либо использую это неправильно, либо что-то не понимаю. Как я могу передать функцию sendPrompt через { children }?

Ответ или решение

Чтобы передать функцию sendPrompt дочерним компонентам в вашем приложении на Next.js, вы можете использовать метод React.Children.map в сочетании с React.cloneElement, как вы уже пытались. Однако важно корректно управлять типами и убедиться, что дочерние компоненты обрабатывают переданные им пропсы.

Ваш первоначальный подход почти правильный, но он нуждается в некоторых корректировках. Давайте посмотрим на решенную версию вашего кода:

'use client';
import React, { ReactElement, ReactNode, FC } from 'react';
import { useState } from 'react';
import { usePathname } from 'next/navigation';
import ChatBlock from '../components/ChatBlock/ChatBlock';
import AboutPage from '../dashboard/about/background/page';

interface BoardContainerProps {
  children: ReactNode;
}

const BoardContainer: FC<BoardContainerProps> = ({ children }) => {
  const [selectedPrompt, setSelectedPrompt] = useState('');
  const pathname = usePathname();

  const sendPrompt = (prompt: string) => {
    setSelectedPrompt(prompt);
  };

  // Пробрасываем функции через дочерние элементы
  const childrenWithProps = React.Children.map(children, (child) => {
    if (React.isValidElement(child)) {
      return React.cloneElement(child, { sendPrompt });
    }
    return child;
  });

  return (
    <>
      <>
        {pathname === '/board/about' ? (
          <AboutPage sendPrompt={sendPrompt} />
        ) : (
          childrenWithProps
        )}
      </>

      <ChatBlock prompt={selectedPrompt} />
    </>
  );
};

export default BoardContainer;

Объяснение кода:

  1. Проверка типа Child: Мы используем React.Children.map для обхода всех дочерних элементов. React.isValidElement обеспечивает, что мы работаем только с валидными React-элементами перед тем, как пытаться клонировать их.

  2. Клонирование элемента: Используем React.cloneElement, чтобы передать sendPrompt как пропс каждому дочернему элементу. Это позволяет дочерним компонентам вызывать sendPrompt и обновлять состояние родительского компонента.

  3. Условие для рендеринга дочерних элементов: Если путь равен /board/about, отображается компонент AboutPage, и sendPrompt передается ему в качестве пропса. В противном случае отображаются все дочерние элементы, которые теперь имеют доступ к функции sendPrompt.

Примечания:

  • Убедитесь, что дочерние компоненты, которые будут использовать sendPrompt, действительно настроены на его использование. Например, в дочернем компоненте вы можете получать sendPrompt из пропсов и вызывать его при необходимости:

    const ChildComponent = ({ sendPrompt }) => {
    const handleClick = () => {
      sendPrompt('Ваш текст сообщения');
    };
    
    return <button onClick={handleClick}>Отправить</button>;
    };

Эта структура компонента позволит вам легко добавлять новые дочерние элементы и управлять функцией sendPrompt без необходимости дублирования кода или использования жёстких ссылок на пути.

Оцените материал
Добавить комментарий

Капча загружается...