UI elements have a hierarchical structure. For example, the card in Bootstrap:
<div class="card">
<img class="card-img-top" src="..." alt="Card image cap">
<div class="card-body">
<h4 class="card-title">Card title</h4>
<p class="card-text">
Some quick example text to build on the card title and make up the bulk of the card's content.
</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
</div>
Card block may contain a picture and a body inside. The body, in turn, can consist of a header and text, and the text can be anything. The same applies both to the simplest elements of HTML itself, such as the <div>
tag, and to the rest of Bootstrap's components, such as modal windows and navigation.
HTML conforms to the nature of UI and naturally allows you to create compositions of elements by nesting tags within each other. JSX works the same way. So far, this feature has only been used in the course for embedded components. Now it's time to learn how to repeat this behavior in self-written components. The example is based on the <Alert />
component from Bootstrap.
See the Pen YxgWVm by Hexlet (@hexlet) on CodePen.
In the example above, only the main div is mandatory. The content depends on the specific situation. It's substituted using the children property.
See the Pen js_react_children_alert_component by Hexlet (@hexlet) on CodePen.
Note that the component has come to be used as a paired tag in JSX:
const vdom = (
<Alert>
<p>Whenever you need to, be sure to use margin utilities to keep things nice and tidy.</p>
</Alert>
);
Everything between the opening and closing tags goes inside the children prop.
But beware: the children property's data type depends on the content. In the simplest case, when the tag is used as a single <div />
, this property will be undefined
.
If this content is a string, it'll be inside children. After some processing, anyway. JSX removes whitespace from the beginning and end of a string, including blank lines. The following examples will be displayed in the same way:
<div>Hello World</div>
<div>
Hello World
</div>
<div>
Hello
World
</div>
<div>
Hello World
</div>
Any single child component will also be represented by itself in children. In all other cases, children
will contain an array.
If you look closely at the React documentation, you'll see the following definition of children: "children are an opaque data structure". In other words, you can't unequivocally rely on the type of this prop, since anything can be conveyed on the outside.
This kind of behavior can lead to hard-to-find mistakes. For example, checking this.props.children.length
doesn't always give the number of children. If children
children is a single element, such as a string, then the length
property will return the length of that string.
class MyComponent extends React.Component {
render() {
const { children } = this.props
return <p>Count: {children.length}</p>
}
}
// <p>Count: 4</p>
<MyComponent>Text</MyComponent>
That's why React provides a set of functions designed to manipulate the children
prop. They're all available in React.Children
. These functions are aware of the features of children
, they check the type themselves and make the necessary checks depending on the type of data.
React.Children.map()
You may remember from the lesson on processing collections that when you work with a list, each item needs to be assigned a key
prop. If you don't do this with React.Children.map
, React won't throw a warning. This is done intentionally, because descendants usually don't have unique identifiers.
React.Children.count()
class ChildrenCounter extends React.Component {
render() {
const { children } = this.props
return <p>Count: {React.Children.count(children)}</p>
}
}
// Count: 1
<ChildrenCounter>
Second!
</ChildrenCounter>
// Count: 2
<ChildrenCounter>
<p>First</p>
<ChildComponent />
</ChildrenCounter>
// Count: 2
<ChildrenCounter>
{() => <h1>First!</h1>} // will be skipped because it's not a dom-element
Second!
<p>Third!</p>
</ChildrenCounter>
In addition to the above, you may need to handle child elements before output by changing a part in their props. Of course, this can't be done directly, because props are immutable. You can achieve this behavior by cloning elements with React.cloneElement()
.
The Hexlet support team or other students will answer you.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.
Programming courses for beginners and experienced developers. Start training for free
Our graduates work in companies:
From a novice to a developer. Get a job or your money back!
Sign up or sign in
Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.