
Use a Render Prop - freeman478
https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce
======
wereHamster
People seem to forget that components (stateless components, stateless
functional components or whatever you want to call them) are just functions.
One function calls another function, that's function composition. In plain
JavaScript we do it all the time. But when React is involved people are
suddenly all like: no you can't call that function directly, you have to name
it 'Component' with a capital C and use like '<Component />' and to compose
two Components you have to use a HOC. It's like they all forget how to
JavaScript…

~~~
baddox
An HOC is a plain JavaScript function that accepts a React component (and
maybe other things) and returns a React component. I don’t think your
complaint applies to HOCs, because they are literally an example of your
suggestion to just use plain functions.

------
pspeter3
As a note, it is not safe for a component using a render prop to implement
shouldComponentUpdate. The problem is that the render prop may close over data
that the component is unaware of. The Asana core components used to the use
the render prop pattern until we discovered that issue and also realized it
made tests harder to write. Now we use React.cloneElement instead.

~~~
doomslice
Would you mind elaborating on how React.cloneElement works vs this?

~~~
pspeter3
_On mobile so not a fully detailed response_

I came up with the pattern after discovering the issues with render callbacks
and before Higher Order Components were common. You can achieve the same
result with a Higher Order Component. The only benefit of this approach over
an HOC is that you write a regular Component.

To implement the pattern, you make you Component expect a single child for
it's `children` prop. Let's assume that the child has a `data` prop which is
not set in the parent Component. In render, you return
`React.cloneElement(this.props.children, { data: this.state.valueToInject })`.

The value of a Higher Order Component is that you can usually define a map to
props function.

~~~
acemarke
Yep, I used this approach for a custom form change event buffering component
that can wrap around input fields to allow fast updates while debouncing
dispatching Redux actions:
[http://blog.isquaredsoftware.com/2017/01/practical-redux-
par...](http://blog.isquaredsoftware.com/2017/01/practical-redux-part-7-forms-
editing-reducers/) .

------
catpolice
I started today by setting out to write a response of the form "Certainly you
couldn't rewrite MY HOC library to use render props, look at how it [etc]" and
ended today having rewritten my HOC library to use render props. In the
process, I was able to dramatically simplify the API and remove about a third
of the overall code.

So, uh, thanks.

------
couchand
This technique is very useful, but passing the callback as a prop is an ugly
way to do it. Much cleaner to pass the callback as children [0].

Then, the final example looks like:

    
    
      <Mouse>
        {({ x, y }) => (
          <h1>The mouse position is ({x}, {y})</h1>
        )}
      </Mouse>
    

[0]: [https://discuss.reactjs.org/t/children-as-a-function-
render-...](https://discuss.reactjs.org/t/children-as-a-function-render-
callbacks/626)

~~~
andrewingram
This is how the technique was first popularised by Cheng Lou in react-motion.
But it was generally found that using children made it really inaccessible to
people unfamiliar with the pattern.

I've literally had good developers not understand them until I switched an
example from using children to using a render prop, at which point there's a
big light bulb moment.

So i'll be sticking with the render prop.

~~~
couchand
Maybe you could clarify it for your good developers by giving the example with
children passed as a prop. Then they'll learn two things!

~~~
andrewingram
What's the point? Given that I prefer the aesthetics of the render prop as
well?

------
arenaninja
My biggest issue with HOCs is the hard time you have when things are nested to
high hell. A component like
withThis(withThat(enrich(withState(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(.......
when this component gives you an issue you begin to wonder what HOCs are
really doing for you.

Having a render prop is slightly better but even this escape hatch isn't
foolproof and you'll still end up needing things like onClickOutside.

~~~
couchand
I think a lot of HOC libraries were designed assuming decorators would be
standardized soon. The connect method from react-redux is definitely in that
camp:

    
    
      @connect(mapStateToProps, mapDispatchToProps)
      class Component extends React.Component { ... }
    

has a certain elegance to it.

~~~
acemarke
To some extent, it was - if you look at the earliest versions of Redux,
`connect()` and its predecessor forms were indeed being used as a decorator.

However, I personally advise against using `connect()` as a decorator, for
several reasons:

\- It's still a Stage 2 proposal. Now, the Class Properties syntax isn't final
either (currently Stage 3), which the React team (and I) highly recommend
using. However, the Class Properties syntax seems to be much more stable, the
behavior it's implementing is a lot simpler, and if by chance it happens to
change in the future, it should be relatively easy to code-mod (and the React
team has said they would release a code-mod if that happens). Meanwhile, the
decorators spec has changed several times (including recently), and the Babel
plugins have also had to change behavior and implementation over time.

\- It obscures the real class definition. The standard advice for testing
Redux-connected components is to export the "plain" class separately as a
named export, and `export default connect()(MyComponent)`, then import and
test the plain version. If you use @connect() as a decorator, the plain
version isn't accessible, and testing becomes more difficult.

\- Going along with that, I've seen many questions about why defaultProps and
propTypes don't work right when @connect() is used, and it's because those get
applied to the wrapper component, not the plain component, and thus things
don't work the way you would want them to.

I see no advantages to using connect as a decorator. I encourage people to
write their mapState functions separately anyway for clarity and testability
(instead of inline as an argument to connect), so it's just a matter of moving
the line with `connect` elsewhere in the file and changing the syntax
slightly.

~~~
couchand
I definitely wasn't recommending that usage, just illustrating what it might
look like. I personally think it looks much cleaner, which I would call an
advantage.

I've considered the first point, but hadn't thought about the second and
third. I'm guessing the last point isn't a concern if you're using class
properties for those?

Given all this, if you were redesigning the API today, would you now make
connect take the component as the first parameter instead?

~~~
acemarke
No. The reason why it's written as not just a HOC, but a curried-ish function
is so that you could potentially reuse the "connection" definition for
multiple components:

    
    
        const SpecificConnection = connect(mapState, mapDispatch);
        const ConnectedFirst = SpecificConnection(FirstComponent);
        const ConnectedSecond = SpecificConnection(SecondComponent);
    

Admittedly, I suspect that use case isn't popping up very often. Most of the
time what I see is that a given component type is only connected once, and a
given connection setup is only used with one component. I also don't remember
seeing specific comments by Dan or Andrew saying that's why it's written this
way - I'm inferring the intent from the API definition. Still, it's a
potentially useful capability in the API, so no reason to throw it away.

~~~
couchand
Theoretically that could be useful, but I've also never seen connections used
this way. I have seen components connected in more than one way, but of course
that's no easier with the curried style. And, I'd point out, if someone
actually wanted to share the configuration, it's trivial enough to write:

    
    
      const SpecificConnection = (Component) => connect(Component, mapState, mapDispatch);
    

Not that I'd argue against currying generally -- it's only that it turns out
to be a little awkward here with the various optional parameters to connect,
and also that the connection setup in practice seems to be tightly coupled to
the connected component.

------
reichardt
Neat! Didn't know about this concept. Any more examples of this being used
somewhere?

~~~
tomgp
The technique discussed is a kind of inversion of control, a concept which has
a pretty long history (~30 years according to the wikipedia article). I've
been writing React for a couple of months now and had settled on this method
without knowing it was considered new or in anyway unusual.

------
hguhghuff
What would the code look like to use this approach for Redux.

Could Redux boilerplate be reduced?

~~~
acemarke
Hi, I'm a Redux maintainer.

First, could you clarify what _you_ mean by "Redux boilerplate" in this case?
The phrase gets thrown around frequently, but it means different things to
different people. Just yesterday, I tweeted some thoughts on how you can use
as much abstraction as you want with Redux, and linked to examples of reusable
action creator/reducer logic [0]. I also wrote a pair of blog posts that
discuss the intent behind Redux's design [1], and why common usage patterns
exist [2].

It's worth noting that React-Redux was originally written using a "render
props"-type approach, but was changed to be a Higher-Order Component before it
hit 1.0. In fact, there was a thread a month ago that discussed render props-
based reimplementations of `connect` [3], and in that thread I linked to
several recent examples of people reinventing that wheel as well as prior
discussion of why React-Redux wound up as a HOC.

Finally, earlier this year I opened up an issue to discuss ways that we can
improve both the "getting started" experience for Redux users, as well as
build more powerful abstractions on top of Redux [4]. I'd love more feedback
and ideas (and ideally people from the community volunteering to help us make
things better).

[0]
[https://twitter.com/acemarke/status/928453589739155456](https://twitter.com/acemarke/status/928453589739155456)

[1] [http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-
tao...](http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-
part-1/)

[2] [http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-
tao...](http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-
part-2/)

[3]
[https://news.ycombinator.com/item?id=15427954](https://news.ycombinator.com/item?id=15427954)

[4]
[https://github.com/reactjs/redux/issues/2295](https://github.com/reactjs/redux/issues/2295)

~~~
hguhghuff
I love Redux. I just want it to take less code to use.

~~~
acemarke
Have you looked at any of the existing libraries in the ecosystem? There's
dozens of tools for generating reusable functions like action creators and
reducers [0], managing complex collections [1], and even higher-level
abstractions on top of Redux like Kea [2].

Ultimately, you can put as much or as little abstraction on top of Redux as
you want.

[0] [https://github.com/markerikson/redux-ecosystem-
links/blob/ma...](https://github.com/markerikson/redux-ecosystem-
links/blob/master/action-reducer-generators.md)

[1] [https://github.com/markerikson/redux-ecosystem-
links/blob/ma...](https://github.com/markerikson/redux-ecosystem-
links/blob/master/entity-collection-management.md)

[2] [https://kea.js.org/](https://kea.js.org/)

------
tootie
Even using the term "higher order" that seems like a very OOP solution, while
render props is very functional and thus a better idiom for JavaScript.

~~~
mcaruso
"Higher order" sounds pretty functional to me. Higher order functions and all.
Although I guess the term higher order function is hardly even spoken of in
real functional languages because they're taken for granted.

~~~
seanmcdirmid
The term “higher order” generally indicates complexity and indirection. If you
can solve something first order, then that is much more preferable to a higher
order (function, logic, object) solution. Higher order functions fall into the
same category (you can have them if you want, but you must understand what you
are getting into in terms of complexity).

~~~
simplify
> The term “higher order” generally indicates complexity and indirection.

As do all abstractions.

> If you can solve something first order, then that is much more preferable to
> a higher order solution.

a.k.a. "To abstract, or not to abstract?". It really depends, of course. But I
know for certain I would not want to solve "mapping over an array" without the
Array.prototype.map higher-order function.

~~~
seanmcdirmid
It really depends. Higher order debugging is more of an art than reality, so
if the mapping logic is tricky, I would definitely convert a map into a loop
just so I had better access to debugging. It should never be “use map whenever
possible.” Also, if you drift out of well known functional origami to more
niche uses of function arguments, well, there be the dragons.

