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 }?
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