Stack Overflow Asked on December 22, 2021
Say I have a <Modal>
that takes a <Header>
<Content>
and <Footer>
.
(
<Modal>
<Header>Foo</Header>
<Content>Foo</Content>
<Footer>Foo</Footer>
</Modal>
)
Now, inside my Modal
component I’ll probably have code like the following:
const header = children.find(child => child.type === Header)
In order to get a reference to the rendered header.
Now, what if from the consumer of the modal, I needed a decorated Header
. Let’s just call it DecoratedHeader
// DecoratedHeader
const DecoratedHeader = () => <Header>Foo <Icon type="lock" /></Header>
// consumer
(
<Modal>
<DecoratedHeader />
<Content>Foo</Content>
<Footer>Foo</Footer>
</Modal>
)
The line above wouldn’t work anymore, as DecoratedHeader
type is not Header
. However, it IS rendering a Header
.
It feels like there’s the concept of "interface" which is missing. Ultimately, the Modal
cares for a Header
to be rendered, but if you wrap it under a "custom" component there’s no way for it to know that it is still a Header
.
What am I missing?
EDIT
To expand more about my use cases, I don’t need an alternative solution. I need to know whether React has support for a mechanism equivalent to an interface, where 2 different Components that comply with the Liskov Substitution Principle (meaning they’re swappable) can have a way to be picked by the parent.
Specifically, replacing this "hardcoded implementation" search, with an "interface" search:
-const specificChild = children.find(child => child.type === SomeComponent)
+const componentInterface = children.find(child => ????)
// Get a prop out of that component interface
const { someInterfaceProp } = componentInterface.props;
return (
<div>
{componentInterface} {/* render it on a specific place */}
</div>
)
Assuming the only thing you're going to be doing with these components is rendering them in specific spots of the modal, i would do them as separate props. For example:
const Modal = ({ header, content, footer }) => {
return (
<div>
{header}
<SomethingElseAllModalsHave />
{content}
{footer}
</div>
)
}
// ... used like:
const Example = () => {
return (
<Modal
header={<DecoratedHeader />}
content={<Content>Foo</Content>}
footer={<Footer>Foo</Footer>}
/>
)
}
If you need the modal to not just render the other components, but give them some information too, you could use a render prop. Basically the same as my example above, but now you pass in functions instead of elements
const Modal = ({ header, content, footer }) => {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
{header(isVisible)}
<SomethingElseAllModalsHave />
{content(isVisible)}
{footer(isVisible}
</div>
)
}
// ... used like:
const Example = () => {
return (
<Modal
header={() => <DecoratedHeader />}
content={(isVisible) => <Content>{isVisible ? "Foo" : "Bar"</Content>}
footer={(isVisible) => isVisible ? <Footer>Foo</Footer> : null}
/>
)
}
EDIT:
When you write the JSX <DecoratedHeader/>
, the object that is produced contains no information about <Header>
. It's basically just an object with a type (ie, a reference to DecoratedHeader
) and some props (none in this case). Header only enters the picture when DecoratedHeader is rendered, which won't be until after Modal is rendered.
So whatever the characteristics are that Modal will use to identify what is and is not a header, it needs to be something that is on DecoratedHeader, not just on Header. Perhaps you could add a static property to any component that counts as a header, and then check for that:
const Header = () => {
// Whatever the code is for this component.
}
Header.isHeader = true;
const DecoratedHeader = () => <Header>Foo <Icon type="lock" /></Header>
DecoratedHeader.isHeader = true;
Then you'll look for it something like this (you should use React.Children, because children
is not guaranteed to be an array):
const header = React.Children.toArray(children).find(child => child.type.isHeader);
Answered by Nicholas Tower on December 22, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP