chantastic / reactpatterns

Patterns for React Developers
http://reactpatterns.com
1.73k stars 101 forks source link

Helpful Errors on Children Pass Through #15

Closed iammerrick closed 6 years ago

iammerrick commented 8 years ago

The section about helpful errors on children pass through when using React.Children.only. What helpful errors? I can't really see anything on React's documentation and would love to understand this better!

chantastic commented 8 years ago

oh that's interesting. i always assumed that the error was being thrown from the component but i guess it's getting sent from the compiler...

example

<Only children={<div>one</div><div>two</div>}>

Error

Failed to compile.

Error in ./src/App.js
Syntax error: Adjacent JSX elements must be wrapped in an enclosing tag (9:38)

   7 |     return (
   8 |       <div className="App">
>  9 |         <Only children={<div>one</div><div>two</div>}>
     |                                       ^
  10 |       </div>
  11 |     );
  12 |   }

Interesting.

Admittedly, trying to come up with examples for this seem contrived to me. Do you have any that illustrate the need for Children.only?

chantastic commented 8 years ago

thanks for helping me clarify this :)

hnordt commented 7 years ago

I didn't understand the use of Children.only(children) yet. 😞

chantastic commented 7 years ago

That's my fault. I've done a terrible job of explaining it.

The practical use of Children.only is that you can create a component that doesn't introduce new DOM elements. This is a pretty good description of the use case: https://github.com/facebook/react/issues/4424#issuecomment-122607192

hnordt commented 7 years ago

@chantastic I have a bunch of components that doesn't emit DOM, e.g. https://github.com/smalldots/smalldots/blob/v0.33.0/src/Toggler.js

Maybe I can replace: return this.props.children(api) || null with: return Children.only(this.props.children(api)).

The reason for || null is that sometimes the consumer may render "false":

<Toggler>{({ toggled, toggle }) => !toggled && <button>...</button>}</Toggler>

And as it's a render callback, I really want to enforce one child, so Children.only() is semantic.

chantastic commented 7 years ago

Does this PR make it any clearer? https://github.com/chantastic/reactpatterns.com/pull/19

chantastic commented 7 years ago

@hnordt that seems right to me. I'm not using any places where i don't control what children are. but yah, i just use it any place i want to wrap something with lifecycle functionality, don't want need an HoC, and don't want to emit extraneous DOM.

hnordt commented 7 years ago

@chantastic I would recommend that:

There are times you'll want a component to have only some lifecycle logic and delegate the rendering to the parent component (render callbacks are an example of that behavior).

In this case, to enforce the rendering of only one child and throws an error otherwise, you can use the function React.Children.only:

class SomeLifeCycleWrapper extends React.Component {
  componentDidMount() {
    console.log("I mounted but have no DOM.")
  }

  render() {
    return React.Children.only(this.props.children)
  }
}
hnordt commented 7 years ago

In cases where you're working with state or context, prefer higher-order components or render callbacks.

I don't agree with this because actually render callbacks are good candidates to use Children.only.

hnordt commented 7 years ago

I think we can even rename that pattern to rendering delegation 😝