logicOnAbstractions
logicOnAbstractions

Reputation: 2580

Nested component as argument in React FC / JSX

I have this structure in React FC typescript:

import { BaseLayout } from "./...."
import { MainContent } from "./...."
import { SplitContent } from "./...."
import { VideoFeed } from "./...."
...

<Route path="/foo" element={<BaseLayout ContentComponent={MainContent}/>}

That works fine.

However, one of the child component is supposed to be another nested component, and I can't seem to make it work.

<Route path="/foo" element={<BaseLayout ContentComponent={MainContent}/>}

MainContent takes no props/arguments, but SplitContent expects something like:

export function SplitContent({TopContent}: {TopContent: FC}) { ... }

(I'm just trying to make it work, obv it'll later take a 2nd bottom component as argument but let's just make it work with one first).

So if I try to just pass the SplitContent as component, not a string, TS complains:

<Route path="/foo" element={<BaseLayout ContentComponent={<SplitContent/>}/>}

"Type Element isn not assignable to type FC<{}>"

(On top of SplitContent not receiving an expected TopContent argument).

If I suppress it with ts-ignore, then in the console it doesn't render and I get:

" React.jsx: type is invalid -- expected a string (for built-in component) or a class/function (for composite component) but got: <SplitContent/> "

Upvotes: 1

Views: 212

Answers (1)

Kavian Rabbani
Kavian Rabbani

Reputation: 984

There is a difference between a component (a function/class which might have state, and is likely to return some elements) and rendered elements (<Foo />). In this case, what you need to provide is a component. Not a rendered element:

<Route
  path="/foo"
  element={
    <BaseLayout
      ContentComponent={() => (
        <WrapperComponent WrappedComponent={SomeConcreteComponent} />
      )}
    />
  }
/>

notice that

() => <WrapperComponent WrappedComponent={SomeConcreteComponent}/>

is a component which in turn, renders something. It's not the rendered thing.

I'd suggest not making it in render though, if possible. Create it outside of your main component as a new component. And use it here. Like:

const MyComponent = () => <WrapperComponent WrappedComponent={SomeConcreteComponent}/>;

// And inside your main component
<Route
  path="/foo"
  element={
    <BaseLayout
      ContentComponent={MyComponent}
    />
  }
/>

Upvotes: 2

Related Questions