0

I'm using functional components. I have built my separate components and am placing them into my index.js file.

Right now the structure of the components is (pseudocode):

<App stateVariable1={x} stateVariable2={y} stateVariable3={z}>
  <ChildOne props1={x} props2={y} />
  <ChildTwo props1={z}/>
</App>

ChildOne and ChildTwo are currently rendered within App.js, and so passing the state variables to the two children is very easy.

I'd like to extract the rendered children into index.js and replace the two children with a {props.children} within App.js, to increase the SRP of the component and make it more reusable.

However I am struggling with understanding how to pass multiple state variables as multiple props to props.children within my index.js file.

I have read the react documentation for Render Props and that deals with one prop being passed to a single child, rather than multiple children which want to receive different props.

How do I go about achieving what I would like to do?

UPDATE

I have attempted to use render props as per the accepted answer here: How to pass props to {this.props.children}

My index.js looks like this:

ReactDOM.render(
  <React.StrictMode>
    <App>
      {(stateVariable1) => (
        <Fragment>
          <ChildOne props1={stateVariable1} props2={stateVariable2} />
          <ChildTwo props3={stateVariable3} />
        </Fragment>
      )}
    </App>{' '}
  </React.StrictMode>,
  document.getElementById('root')
);

However all of the props/stateVariables through undefined errors, as of course they're not defined in index.js.

Where am I going wrong?

Update 2: Solution

I passed all of the state variables as arguments to the render props and this has solved the issue:

ReactDOM.render(
  <React.StrictMode>
    <App>
      {(stateVariable1, stateVariable2, stateVariable3) => (
        <Fragment>
          <ChildOne props1={stateVariable1} props2={stateVariable2} />
          <ChildTwo props3={stateVariable3} />
        </Fragment>
      )}
    </App>{' '}
  </React.StrictMode>,
  document.getElementById('root')
);

Is there a way to deconstruct this so that I don't need to pass each argument into the callback? I am using functional components so state is stored in multiple variables rather than within the class components' state object.

  • 1
    You effectively answered your own question :D. You could still do a `useState({ stateVar1: ..., stateVar2: ... })` to pass them as a props object, but beware that you need to replace the object in App.js with a new object, else changes won't be detected. In your child-as-a-function you could then do like: `({ stateVar1, ...otherVars }) => (<>>)` – webketje May 14 '21 at 08:58
  • @Tyblitz thanks, where would I use the `useState` function? – LearningPython May 14 '21 at 10:29
  • @Tyblitz it WAS working as described above, It's now not, and I'm receiving the error: ```Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.``` – LearningPython May 14 '21 at 10:29
  • Sorry I thought it was evident the `useState` was in `App.js`. Your error seems like you forgot to call `props.children(stateVars)` in `app.js`. – webketje May 14 '21 at 11:44

2 Answers2

1

I think you can avoid having to use props.children altogether.

If you have a structure like below, your SRP principle will still hold true.

  • App
    • Child 1
    • Child 2

What the above lines mean is that rather than going with render props pattern or having props.children, you can have your Child1 and Child2 as components within your App component.

Something like

const App = (props) => {
 return (
   <>
     <Child1 {...props} />
     <Child2 {...props} />
   </>
 );
}

If you want to make code more generic and have a single Children component, you can extract all children out to index.js file so that your App component remains untouched, but that might not be needed to be honest.

jaybhatt
  • 550
  • 2
  • 7
  • Thanks. That is how my App is currently structured, it just seems like an antipattern to have multiple components render responsibility fall to this component. Plus it would be good to get to grips with render props. – LearningPython May 14 '21 at 08:42
  • I think I solved my own issue. I don't know whether you can answer the final question I have, on how to deconstruct/spread all arguments in the render prop callback? – LearningPython May 14 '21 at 08:51
  • 1
    It is not at all an anti pattern to have a parent hold the responsibility to render all children, rather most cases you will have that scenario. Even in terms of readability, the previous one was much more readable in my opinion, render props just reduces readability a bit. And as for destructuring, there is no direct way for how the current code is written :( – jaybhatt May 14 '21 at 09:17
0

I solved my issue with some help from Tyblitz.

ReactDOM.render(
  <React.StrictMode>
    <App>
      {({stateVar1, stateVar2, ...stateVars}) => (
        <Fragment>
          <ChildOne props1={stateVar1} props2={stateVar2} />
          <ChildTwo {...stateVars} />
        </Fragment>
      )}
    </App>{' '}
  </React.StrictMode>,
  document.getElementById('root')
);

I then added a useState() function to my App component and passed all state variables in as an object.

Finally, I passed this new state variable in as an argument to my return call:

return <div className='App'>{props.children(stateVars)}</div>;