Is there any way to grab all of the bar
properties in <Wrapper/>
below 'statically', e.g. without rendering?
import React from 'react';
import ReactDOM from 'react-dom';
class Foo extends React.Component {
render() {
return(
<div>
<span bar="1" /> // want to collect this 'bar'
<span bar="2" /> // want to collect this 'bar'
</div>;
);
}
}
class FooTuple extends React.Component {
render() {
return(
<div>
<Foo />
<Foo />
</div>;
);
}
}
class Wrapper extends React.Component {
render() {
React.Children.forEach(this.props.children, child => {
console.log(child.props); // can only see <FooTuple/> not <Foo/>
});
return(
<div>
{this.props.children}
</div>;
);
}
}
ReactDOM.render(
<Wrapper>
<FooTuple />
</Wrapper>,
document.getElementById('app'));
Here's a webpackbin with a naive attempt that tries to iterate over child.children
which obviously doesn't work, but it's here if it's helpful:
http://www.webpackbin.com/EySeQ-ihg
Yes it is very much possible and very much useful, in case of lazy loading components.
In normal rendering, React does not care whether “props changed” - it will render child components unconditionally just because the parent rendered!
Taking advantage of the fact that components don't have to render anything has made my components much more modular and compliant with the Single Responsibility Principle. As with any other code, its best when React components have only one major purpose.
For class components, the prop is called in their render function. For functional components, the prop is called in the component function. We can use any prop for the function, not just children , so using the word “children” can be inaccurate.
TL;DR; Nope that's not possible.
--
I've once encountered the same problem trying to traverse a tree of deeply nested children. Here are my scoop outs:
children
are what's placed inside the jsx
open and close tags, or injected directly in the children prop. other than that children
prop would be undefined
.
<div className="wrapper">
// Children
<img src="url" />
</div>
/* OR */
<div classname="wrapper" children={<img src="url" />}>
children
are an opaque tree-like data structure that represents the react elements' tree, it's likely the output of React.createElement
that the jsx
implements when transpiling.
{
$$typeof: Symbol(react.element),
type: 'div',
key: null,
ref: null,
props: {
className: "wrapper",
children: {
$$typeof: Symbol(react.element),
type: 'img',
key: null,
ref: null,
props: { src: 'url' },
}
}
}
Creating React
elements doesn't mean that they are instantiated, think of them like a descriptor that React
uses to render those elements. in other words, instances are taken care off by React
itself behind the scenes.
Let's take your example and try to traverse the whole tree.
<Wrapper>
<FooTuple />
</Wrapper>
The opaque children object of these elements would be something like this:
{
$$typeof: Symbol(react.element),
type: Wrapper,
key: null,
ref: null,
props: {
children: {
$$typeof: Symbol(react.element),
type: FooTuple,
key: null,
ref: null,
props: {},
}
}
}
As you can see FooTuple
props are empty for the reason you should know by now. The only way to reach it's child elements is to instantiate the element using it's type
to be able to call it's render method to grab it's underlying child elements, something like this:
class Wrapper extends React.Component {
render() {
React.Children.forEach(this.props.children, child => {
const nestedChildren = new child.type(child.props).render();
console.log(nestedChildren); // `FooTuple` children
});
return(
<div>
{this.props.children}
</div>;
);
}
}
This is obviously not something to consider at all.
There is no clean way to augment deeply nested children or grab something from them (like your case). Refactor your code to do that in a different manner. Maybe provide a setter function in the context
to set the data you need from any deep child.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With