I am creating a React component for my InertiaJS app that accepts children with a callback for passing the parent’s state to the current children.
To clarify, here’s a snippet from the Menu component of HeadlessUI.
<MenuButton as={Fragment}>
{({ active }) => <button className={clsx(active && 'bg-blue-200')}>My account</button>}
</MenuButton>
As shown, the children are wrapped inside a callback with an active
prop. This prop allows us to style the button’s className
as the state changes.
Here’s a simplified version of a component where you can pass children with a callback or just normal children:
import React, { ReactNode, useState } from "react";
interface ChildrenProps {
count: number;
increment: () => void;
decrement: () => void;
}
interface Props {
children: ReactNode | ((props: ChildrenProps) => ReactNode);
}
export const Counter = ({ children }: Props) => {
const [count, setCount] = useState<number>(0);
const increment = (): void => {
setCount((oldValue) => oldValue + 1);
};
const decrement = (): void => {
setCount((oldValue) => oldValue - 1);
};
// Check the type of children; if it's a function, build it with ChildrenProps
// Otherwise, just pass it as a normal component
let resolvedChildren =
typeof children === "function"
? children({ count, increment, decrement })
: children;
return resolvedChildren;
};
You can now use it like this:
<Counter>
{/* these props all typed properly */}
{(count, increment, decrement) => (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
)}
</Counter>
Or you can pass a normal component as children without a callback:
<Counter>
<div>
<p>Static content</p>
</div>
</Counter>
Ref: Medium