Patrick W. McMahon
Patrick W. McMahon

Reputation: 3561

handling props in a deconstructed child

I have a React component that clones its children with additional props. I'm using the standard childrenWithProps method that works great if your child is another react component but no clear way of doing this without a direct react component as the child.

<DataCmp>
   <Fragment>
      <h1>Example Code</h1>
      <div>{isLoggedIn}</div>
   </Fragment>
</DataCmp>

In this example, I have adding the prop myData to the props of its children. However, this doesn't work. The child doesn't see the value. It will say myData is not set when it's passed in by props.

So I attempted this:

<DataCmp>
   <Fragment>
      <h1>Example Code</h1>
      <div>{this.props.isLoggedIn}</div>
   </Fragment>
</DataCmp>

This brings up errors as it has no idea what this.props.myData is.

My next attempt was to wrap the child in an inline function and get the prop from that.

<DataCmp>
  {({ isLoggedIn}) => (
   <Fragment>
      <h1>Example Code</h1>
      <div>{isLoggedIn}</div>
   </Fragment>
  )}
</DataCmp>

While this doesn't bring up any errors; The child component is never rendered.

I'm working on updating and modernizing someone else old Github project. here is the link to my project and the wrapping component is Wallet.jsx the location that it's being used is index.jsx

The children are rendered as such:

renderChildren = () => {
        const { children } = this.props;
        const { accounts } = this.state;
        const handleLogin = () => this.login();
        const childrenWithProps = React.Children.map(children, (child, index) => {
            if(typeof child == 'object') {
                return React.cloneElement(child, {
                    key: index,
                    loginFn: () => handleLogin(),
                    isLoggedIn: accounts[0] !== '0x0',
                    accounts,
                });
            } else {
                return child;
            }   
        });
        return childrenWithProps;
    } 

Upvotes: 1

Views: 212

Answers (2)

MarcRo
MarcRo

Reputation: 2473

React.Children.map(children, fn) only iterates over valid react elements

This excludes, for example, functions passed as child. Passing a function-to-be-rendered as prop to a component is known as Render Props pattern. React.Children.map will not iterate over this, hence, your third option returned null.

Fix it by checking whether children is a valid ReactElement first and render it accordingly:


// wallet.tsx
...
const additionalProps = { ... };
if (React.isValidElement(children)) {
  return React.Children.map(children,
    (child, i) => React.cloneElement(child, { key: i, ...additionalProps });
} else {
  // handle null, strings, undefined, booleans etc
  return typeof children === 'function' ? children(additionalProps) : children;
}
...

// index.tsx

<Wallet ...>
  {/* either a function */}
  {(additionalProps) => console.log(additionalProps)}

  {/* or components */}
  <Layout>
    ...
  </Layout>
</Wallet>

Note that React.isValidElement() also returns true for HTML-Elements. Which will receive the props but you obviously cannot add custom-logic. But let's say you pass a style props, it will be applied.

Upvotes: 0

maxpsz
maxpsz

Reputation: 604

I guess the error may not be in the destructuring, but in how you are using childrenWithProps.

It would be useful if you shared a condesandbox representing the problem with dummy data, so we can take a look there at that part too.

Upvotes: 1

Related Questions