Reputation: 3758
I have a need to be able to know how child nodes are associated with their parents and to be able to reference them...
For example:
```
<CustomComponent>
<h1>Hello, World!</h1>
<CustomComponent>
<h1>One</h1>
</CustomComponent>
<CustomComponent>
<h1>Two</h1>
<CustomComponent>
<h1>Two One</h1>
</CustomComponent>
<CustomComponent>
<h1>Two Two</h1>
</CustomComponent>
</CustomComponent>
</CustomComponent>
```
As you can see above, there is an inherent tree structure to these components (composite or host) and I'd like to be able to know, at the very minimum, how CustomComponent
s are nested throughout an entire React application.
I tried a naive solution with the following:
```
<CustomComponent id="0">
<h1>Hello, World!</h1>
<CustomComponent id="0:1">
<h1>One</h1>
</CustomComponent>
<CustomComponent id="0:2">
<h1>Two</h1>
<CustomComponent id="0:2:1">
<h1>Two One</h1>
</CustomComponent>
<CustomComponent id="0:2:2">
<h1>Two Two</h1>
</CustomComponent>
</CustomComponent>
</CustomComponent>
```
Obviously this is not an optimal approach as the user would have to define the entire tree structure with id
s and in addition, if the order in which these components mount into the DOM is not linear so it would be hard to reconstruct the tree and be able to perform logic on their relationship to one another.
Any ideas on how to go about this? Thank you for your time and assistance.
Upvotes: 3
Views: 587
Reputation: 130132
While I think it's an abuse of react, you can use context
to maintain parent-child relationships:
let componentId = 0
class CustomComponent extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {componentId: componentId++};
}
getChildContext() {
return {
parentId: this.state.componentId
};
}
render() {
return (
<div>
Parent ID: {this.context.parentId}
{this.props.children}
</div>
);
}
}
CustomComponent.contextTypes = {
parentId: React.PropTypes.number
}
CustomComponent.childContextTypes = {
parentId: React.PropTypes.number
};
You can then use that to generate id
or a className
to search for the component.
You can probably use that to create increasing identifiers in the form 1-2-3...
but that's just an extension of the idea.
You can also use a pure HTML method. In your componentDidMount
you can find all direct descendants with a specific class
, e.g. using xPath (document.evaluate
) but that solution will be a bit slow.
Another possibility is to map the children in render
, adding the index as property manually. An example (I hope it covers all possibilities).
class CustomComponent extends React.Component {
mapChildren(children) {
const thisIndex = this.props.index;
return React.Children.map(children, child => {
if (child.type === CustomComponent) {
return React.cloneElement(child, {
index: thisIndex + "-" + this.childIndex++
});
} else if (child.props && child.props.children) {
return React.cloneElement(child, {
children: this.mapChildren(child.props.children)
});
} else {
return child;
}
});
}
render() {
this.childIndex = 1;
const newChildren = this.mapChildren(this.props.children);
return (
<div>
This ID: {this.props.index}
{newChildren}
</div>
);
}
}
CustomComponent.propTypes = {
index: React.PropTypes.string
};
CustomComponent.defaultProps = {
index: "1"
};
ReactDOM.render(
<CustomComponent>
<h1>Title 1</h1>
<CustomComponent>
<h1>Title 2</h1>
</CustomComponent>
<CustomComponent>
<h1>Title 3</h1>
<CustomComponent>
<h1>Title 4</h1>
</CustomComponent>
<CustomComponent>
<h1>Title 5</h1>
</CustomComponent>
<div>
Wrapping div
<CustomComponent>
<h1>Title 6</h1>
</CustomComponent>
</div>
</CustomComponent>
</CustomComponent>,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
However, this could have some performance implications.
Upvotes: 1