Reputation: 4200
I create a component which is a parent component of another: Container
is parent and Container.Data
children. In this way I can add a structure to my component.
const Container = ({ children }) => {
return (
<div>
<h1>Container</h1>
{children}
</div>
);
};
const Data = ({ children }) => {
return <div>{children}</div>;
};
Container.Data = Data;
export { Container };
///
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Container>
<Container.Data>item1</Container.Data>
<Container.Data>item2</Container.Data>
</Container>
<Container.Data>item3</Container.Data>
</div>
The issue is next: <Container.Data>item3</Container.Data>
is outside the component and it works, but I want in the case when I will add the <Container.Data>item3</Container.Data>
outside the parent Container
, to get an error. So all items should be only inside Container
.
Question: How to do this?
demo: https://codesandbox.io/s/stupefied-dubinsky-6lh8v?file=/src/Container.js:27-264
Upvotes: 1
Views: 352
Reputation: 714
You can pass the extra param in children of container and check in Data component that if that component has the prop or not. If it does not then ignore the child.
import React from "react";
const Container = ({ children }) => {
return (
<div className="parent-container">
<h1>Container</h1>
{React.Children.map(children, (child, i) => {
return React.cloneElement(child, {
fromParent: true
});
})}
</div>
);
};
const Data = ({ children, fromParent }) => {
if (!fromParent) {
return null;
}
return <div>{children}</div>;
};
Container.Data = Data;
export { Container };
https://codesandbox.io/s/naughty-elion-42ppn?file=/src/Container.js:0-474
Upvotes: 1
Reputation: 203532
Use a React Context to create a context for the Data
component to "check-in" with. Provide a defaultValue
to the context that is overridden by a "real" value by the provider. If a component subscribed to the context doesn't have a matching Provider above it in the tree it uses this default value.
import React from "react";
const ContainerContext = React.createContext("containerless");
const Container = ({ children }) => {
return (
<ContainerContext.Provider value="Container">
<div>
<h1>Container</h1>
{children}
</div>
</ContainerContext.Provider>
);
};
const Data = ({ children }) => {
const containerContext = React.useContext(ContainerContext);
React.useEffect(() => {
if (containerContext === "containerless") {
throw new Error("Data Container not contained in Container component");
}
});
return <div>{children}</div>;
};
Container.Data = Data;
export { Container };
App
Move the "item3" data container in and out of Container
to test.
function App() {
return (
<div className="App">
<Container>
<Container.Data>item1</Container.Data>
<Container.Data>item2</Container.Data>
</Container>
<Container.Data>item3</Container.Data>
</div>
);
}
Upvotes: 2
Reputation: 1631
In order to achieve better readability, I would suggest having each element structured in different files and then importing them when needed like so:
import Container from './Container';
Then you'll be able to use them in your App
file without problems.
Upvotes: 0