Imagine having React ponent
function List() {
return (<ul>
<li>1</li>
<li>2</li>
</ul>
);
}
I would like to create high-order ponent which modifies, for example, styles of all li
node.
function makeRed(ponent) {
return function(props) {
const element = React.createElement(ponent, props);
return React.cloneElement(
element,
element.props,
React.Children.map(
element.props.children,
ch => React.cloneElement(ch, {
...ch.props,
style: {
backgroundColor: "red"
}
},
ch.props.children
)
)
);
}
}
But. This doesn't work. Children are empty.
Interesting that this works if I create ponent directly, like
...
const element = <ul><li>1</li><li>2</li></ul>;
...
Question: how to access a children and grandchildren of any React element?
Imagine having React ponent
function List() {
return (<ul>
<li>1</li>
<li>2</li>
</ul>
);
}
I would like to create high-order ponent which modifies, for example, styles of all li
node.
function makeRed(ponent) {
return function(props) {
const element = React.createElement(ponent, props);
return React.cloneElement(
element,
element.props,
React.Children.map(
element.props.children,
ch => React.cloneElement(ch, {
...ch.props,
style: {
backgroundColor: "red"
}
},
ch.props.children
)
)
);
}
}
But. This doesn't work. Children are empty.
Interesting that this works if I create ponent directly, like
...
const element = <ul><li>1</li><li>2</li></ul>;
...
Question: how to access a children and grandchildren of any React element?
Share Improve this question asked Aug 9, 2018 at 22:23 STOSTO 10.7k8 gold badges33 silver badges32 bronze badges 4-
9
This very much seems like an anti-pattern in React. Why not instead create a
List
ponent that can accept styles as a prop, and a higher-order ponent that passes said prop to them? – Hamms Commented Aug 9, 2018 at 22:28 - 2 can you give a more practical example of what you want to do? your example is much more easily achieved with plain old css – azium Commented Aug 9, 2018 at 22:46
- I need this to add "sortable (draggable)" behavior to any element with children with HOC. I know this is not a pure react way for normal ponents but that's just the simplest example illustrates problem. – STO Commented Aug 10, 2018 at 8:22
- @STO this can be done as Hamms already said. – Jordan Enev Commented Aug 27, 2018 at 8:10
1 Answer
Reset to default 4 +200This is an anti-pattern, as @hamms has pointed out. There are better ways to implement themes in React using plain old CSS.
Said that, here's a hack to a working example of your use case - https://codesandbox.io/s/ymqwyww22z.
Basically, here's what I have done:
Make
List
a class based ponent. It's not too much trouble to wrap a functional ponent into one.import React, { Component } from "react"; export default class List extends Component { render() { return ( <ul> <li>1</li> <li>2</li> </ul> ); } }
Implement
render
in the dynamic classRed<Component>
to first fetch the element-tree returned from the base Component's render and then edit it.import React from "react"; export default function makeRed(Component) { return class RedComponent extends Component { constructor(props) { super(props); RedComponent.displayName = `Red${Component.name}`; } render() { let ponentElement = super.render(); let { children, ...rest } = ponentElement.props; children = React.Children.map(children, child => { return React.cloneElement(child, { ...child.props, style: { backgroundColor: "red" } }); }); return React.cloneElement(ponentElement, rest, ...children); } }; }
How's this different from the createElement
version of makeRed
?
As makeRed
returns a HOC, when you use it in your App
ponent, you don't assign props to it. Something like this...
function App() {
return <RedList />; // no props!
}
So inside the dynamic ponent function, where you use createElement
to create a new instance, ponent.props
don't carry any children. Since List
creates its own children, you need to grab those and modify them, instead of reading children from props
.