skip to Main Content

I’m trying to build an app with a chat block that does not refresh on page change. The child pages contain prompts to save the user from typing common responses. All my pages live within a /board directory that has a top level page called ‘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 sendPrompt={sendPrompt} /> }
        {/* This works but not suitable for more than 2 child pages */}

        {/* childrenWithProps */}
        {/* this doesn't work either */}

        {/* children */}
      <>

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

export default BoardContainer;

I figured a work-around using usePathname and essentially hard coding the child value and passing the sendPrompt function, but this is cumbersome.
I tried using React.cloneElement, but I’m either using this incorrectly or misunderstanding something. How can I pass the sendPrompt function via { children }?

2

Answers


  1. You’ll use a callback function…
    <ChatBlock prompt={selectedPrompt} setSelectedPrompt={setSelectedPrompt} />
    Then for the prop in chat block component itself…
    setSelectedPrompt: React.Dispatch<React.SetStateAction<string>>;
    Then for the place you’re using it in your chat block code…
    onClick={() => setSelectedPrompt('prompt1')}

    Login or Signup to reply.
  2. One way to solve this is to use useContext link

    Create a provider in BoardContainer

    export const SendPromptContext = createContext<Function>(() => {});
    
    const [selectedPrompt, setSelectedPrompt] = useState('initial prompt');
    const sendPrompt = (prompt: string) => {
      setSelectedPrompt(prompt);
    };
    
    <SendPromptContext.Provider value={sendPrompt}>
      <div>{selectedPrompt}</div>
      {children}
    </SendPromptContext.Provider>
    

    Subscribe to the context and get sendPrompt in your client side components like this:

    const sendPrompt = useContext(SendPromptContext);
    

    Working sample: https://stackblitz.com/edit/stackblitz-starters-yqbyrm?file=app%2Flayout.tsx

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search