Tim Roberts Tim Roberts - 3 months ago 20
React JSX Question

Adding Event Handlers/Props to inner markup in React

I am trying to create a Repeatable component in React. The goal is that it can be given any

group
that is a valid React component and take any list of
callbacks
and apply those callbacks to the
group
element. Below is what I have that kinda works and I will explain how it is different than what I am wanting to do after. Link to codepen:

http://codepen.io/cirsca/pen/vKqzjZ?editors=0011

My wrapping component:

class App extends React.Component {
constructor(props){
super(props)
this.state = {
callbacks: {
onClick: (e) => console.log('I was clicked!')
}
}
}

render(){
return <div>
<Repeatable
group={<Group />}
{...this.state.callbacks}
/>
</div>
}
}


My Repeater Component:

class Repeatable extends React.Component {
constructor(props){
super(props)
const {group, ...callbacks} = this.props
this.state = {
group: group || <div>This is awesome!</div>,
list: [group],
callbacks
}
}

addGroup = () => this.setState({
list: [
...this.state.list,
this.state.group
]
});

render(){
return <div id={this.props.cssID || 'react_repeatable'}>
<button onClick={this.addGroup}>Add Group</button>
<div className="repeated__list">
{this.state.list.map(el => {
return React.cloneElement(el, {...this.state.callbacks})
})}
</div>
</div>
}
}


And my dummy Group component is:

const Group = ({className, ...callbacks}) => (
<div className={className || 'repetable__block'} {...callbacks}>
<p>Here is a repeatable group!</p>
</div>
)


This works as long as I want to attach my callbacks to the root element in the
Group
component. What I am wanting is a way that my
callbacks
can be targeted towards the nested
p
tag or that I can not have a billion checks of
onClick={callbacks.onClick ? ....}
per each underlying component and action ( in case a new onAction comes out that I did not put into my code ).

I feel like I'm overly complicating things here but am unsure of where to look to find that out or what to google to get started. Any and all help is greatly appreciated.

Answer

You can recursive clone children and pass props down

const recursiveCloneChildren = (children, props) => {
  return React.Children.map(children, child => {
    let childProps = {};
    if (React.isValidElement(child)) {
      childProps = props;
    }
    childProps.children = this.recursiveCloneChildren(child.props.children);
    return React.cloneElement(child, childProps);
  })
}

const Group = ({className, children, ...callbacks}) => (
  <div className={className || 'repetable__block'} {...callbacks}>
    {recursiveCloneChildren(children, callbacks)}
  </div>
)