Component that returns a JSX element that returns a single function

Viewed 35

I was wondering if it was possible to type a JSX element (or component) that returns a single function as its child?

An example:

interface ComponentChildrenProps {
  someProp: string;
}

const Component: React.FC<ComponentProps> = ({ children }): JSX.Element => {
  const propsForChildren: ComponentChildrenProps = { ... }

  const childrenFunction = children as (props: ComponentChildrenProps) => ReactNode;
  
  return <div>{children && childrenFunction(propsForChildren)}</div>
}

So in this case, I return only a JSX.Element, but I was wondering if it was possible to actually type the Component to return a JSX.Element that outputs a single function as its child.

Kind of like

const Component = (props): JSX.Element<{(props: propsForChildren) => ReactNode}> => 
{ ...component code }

In essence, I would like TS to autocomplete the Component props when I open the function inside of it, and I was wondering if that was possible

Edit:

This is a kind of generic form component. I'll attach a semi practical use of it below (I stripped it from most of its inner workings not to add too much confusion):

const Form: React.FC<FormProps> = (props): JSX.Element | null => {
  const { ... } = props;

  const { handleSubmit, control, watch, formState } = useForm(props)

  const childrenFunction = children as (props: FormChildrenProps) => ReactNode;

  if (!children) return null;

  return (
    <form id={id} onSubmit={handleSubmit(onValidSubmit)}>
      {childrenFunction({ control, formState, watch })}
    </form>
  );
};

Usage:

<Form>
   {({ control, watch }: FormChildrenProps) => (
     <section>
       <TextField control={control} label="foo" name="foo" />
       <TextField control={control} label="bar" name="bar" />
     </section>
   )}
</Form>
1 Answers

I guess it depends on the side effect of your function. You can certainly return multiple items from the component. JSX is merely a template language overlaid on Javascript. If you are wanting to just run the function, it may look like this:

Running the function

function Component({children}:{children:React.FC<ComponentProps>}): JSX.Element {
...
    return (
        <div>
        {children}
        {childFunction()} // or, to print to div {childFunction().toString()}
        </div>
    );
}

I'm not entirely sure what the purpose of the function could be, if using it as a side-effect, you should look at useEffect() or this blog here

If you just want to pass it along to the child to use, you could set up a state object or just pass it as a prop

//Parent
function ParentComponent(...): JSX.Element {
    const funcForChild = () => void; //example function, either received as prop or defined here.
    //using state
    const [funcState, setFuncState] = useState(() => void);
    ...
    return (
        <>
            <ChildComponent function={funcForChild} />
            <ChildComponent function={funcState} />
        </>
    );
}

//Child
function Child({funcToRun}:{funcToRun: (() => void)}) //whatever your type is. Maybe unknown
    useEffect(() => {
        funcToRun() //runs on mount
    },[]);

    return(
        ...
    );

Just returning the elements:

const Component = ({children}) => {
    const jsx = <h1>Hello, World!</h1>
    ...
    return({children, jsx});
}

Though, I don't know why you would use the latter.

Could use more clarification. Thanks.

Related