Bilal Shafi
Bilal Shafi

Reputation: 166

Passing props to Children of Children (nested component tree)

I am not sure if this could be possible but here is the thing that I want to achieve in Reactjs:

<Comp1>
  <Comp2 />
  <Comp3>
    <Comp4 />
  </Comp3>
</Comp1>

So what I want to do is, I want to inject a prop from Comp1 component down to Comp4 component using React.cloneElement API. In that way, if I want to propagate a prop or set of props to may be a child at Level 6 down in hierarchy, I should be able to do that.

But what I managed to achieve uptil now is, the props are injected to the immediate Children but not the children of children.

Here is my effort to do it: https://codesandbox.io/s/x2q06l5z64

I guess I've made the problem clear, if there is a confusion, you can ask it away.

Upvotes: 4

Views: 1917

Answers (3)

Keith
Keith

Reputation: 24181

Update: Ther HOC, I've used inthe example is not what React call a HOC, not sure of the proper name for this sort of code,. Scoped Component maybe. On a side note, the reasons given for HOC's in the doc's, eg. (code reuse). Can be done much nicer now with Hooks.

To expand on the HOC (Higher Order Component) idea,.

Here is a simple example.

Notice how Comp4 didn't have to have props passed down from Comp2 & Comp3, this is because we have made Comp1 into a HOC. So although you could use Context's, the ideal would be to use HOC's were possible,. This is because each Component can always be treated in isolation, that makes debugging an re-usability so much better.

function Comp2(props) {
  return <div>Comp2: <br/>{props.children}</div>
}

function Comp3(props) {
  return <div>Comp3: <br/>{props.children}</div>
}

function Comp4(props) {
  return <div>Comp4: {props.value}<br/>{props.children}</div>
}

function Comp1(props) {
 return <div>
  Comp1: {props.value}
  <Comp2>
    <Comp3>
      <Comp4 value={props.value}/>
    </Comp3>
  </Comp2>
 </div>
}



ReactDOM.render(
  <Comp1 value={"This get passed to everything using a HOC"}/>,
  document.querySelector("#mount")
);
div {
  margin-left: 1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="mount"/>

Upvotes: 0

Sagiv b.g
Sagiv b.g

Reputation: 31024

This is a classic use case for react's context API.

const MyContext = React.createContext('');

class Parent extends React.Component {
  render() {
    const { message } = this.props;
    return (
      <MyContext.Provider value={message}>
        {this.props.children}
      </MyContext.Provider>
    )
  }
}

class Child extends React.Component {
  render() {
    return (
      <MyContext.Consumer>
        {(message) => (
          <div style={{ border: '1px solid #ccc' }}>
            <h3>Child</h3>
            {message}
          </div>
        )}
      </MyContext.Consumer>
    )
  }
}

class App extends React.Component {
  state = { message: 'default message' }
  handleChange = ({ target }) => this.setState({ message: target.value })
  render() {
    const { message } = this.state;
    return (
      <div className="App">
        <input value={message} onChange={this.handleChange} />
        <Parent message={message}>
          <div className="some-child">
            <div className="some-child">
              <div className="some-child">
                <div className="some-child">
                  <Child />
                </div>
              </div>
            </div>
          </div>
        </Parent>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"/>

Upvotes: 4

Gabriele Petrioli
Gabriele Petrioli

Reputation: 195982

You have your ternary logic reversed.

You original code was

return childzFurtherChildren
      ? React.cloneElement(child, { ...newProps, hello: true })
      : React.cloneElement(child, { ...newProps, hello: true }, childzFurtherChildren);

but it needs to be

return childzFurtherChildren
      ? React.cloneElement(child, { ...newProps, hello: true }, childzFurtherChildren)
      : React.cloneElement(child, { ...newProps, hello: true });

Because when you have further children (that have been processed) you then need to add them to the cloned element.

Updated demo at https://codesandbox.io/s/8n26lq6mj9

Upvotes: 3

Related Questions