-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Add doc about Render Props #355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Deploy preview for reactjs ready! Built with commit dcd53fd |
f1e7c2f
to
8cc2776
Compare
7168d70
to
d5477ad
Compare
@toddsby The main concern with using a render prop is when you use them with pure components, as I've already outlined in the "Caveats" section. If you think there's something else I can add there, I'm all ears. |
permalink: docs/render-props.html | ||
--- | ||
|
||
The term ["render prop"](https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce) refers to a simple technique for sharing code between React components using a prop whose value is a function. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to kick off this page with a simple example and motivation to frame the conversation. The HOC doc starts with:
Concretely, a higher-order component is a function that takes a component and returns a new component.
const EnhancedComponent = higherOrderComponent(WrappedComponent);
...explain at high-level where and when HOCs are useful...
Paraphrasing the example code from @thejameskyle's #349, how about this:
The term "render prop" refers to a simple technique for sharing code between React components using a prop whose value is a function.
A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>
By inverting control of the render method to the consumer of the component, render props provide a flexible way to encapsulate and share logic between components.
Libraries that use render props include React Router and the enhanced input library Downshift.
In this document, we’ll discuss why render props are useful, and how to write your own.
} | ||
``` | ||
|
||
In cases where you cannot bind the instance method ahead of time in the constructor (e.g. because you need to close over the component's props and/or state) you should extend `React.Component` instead. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about mentioning some other ways of writing render props at the end?
Using Props Other Than render
Although the examples above use render
, any prop can be used to implement the render prop pattern. For example, the component below passes a function as the children
prop:
<WithTitleAsync url="/my/api/books/123">
{ ({ title }) => <h1>{title}</h1> }
</WithTitleAsync>
class WithTitleAsync extends React.Component {
constructor(props) {
super(props);
this.state = { title: null };
}
componentDidMount() {
fetch(this.props.url)
.then(
({ title }) => { this.setState({ title }) },
(error) => { this.setState({ error }) }
)
}
render () {
if (this.state.error) {
return <span>Title Not Found</span>;
} else if (!this.state.title) {
return <span>Getting Title...</span>
} else {
return this.props.children(this.state.title);
}
}
};
WithTitleAsync.propTypes = {
children: PropTypes.func.isRequired,
};
} | ||
} | ||
``` | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moving this to a separate comment
Here's an idea for an optimization section. I don't think it's entirely necessary for the first pass and could certainly be added later.
Optimizing Render Props
If you've benchmarked your app and discovered that a render prop declaration is a performance bottleneck, here are some techniques to optimize:
- Move inline functions to be declared outside the render.
Before: Inline Render Prop
class ABC extends React.Component {
render() {
<MyComponent render={() => <h1>Hello World</h1>}
}
}
After: Declared Outside Class
class ABC extends React.Component {
render() {
return <MyComponent render={helloWorld} />
}
}
function helloWorld() {
return <h1>Hello World</h1>
}
After: Using Getters
class ABC extends React.Component {
get helloWorld() {
return <h1>Hello World</h1>
}
render() {
return <MyComponent render={this.helloWorld} />
}
}
- Memoize the render function using a utility like Recompose or Lodash.Memoize
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Memoizing will solve the PureComponent caveat mentioned in the doc wouldn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nit: your "Using Getters" example passes a react element as the render prop, not a function)
Thanks for this write-up! This pattern makes things simpler :) |
Is anyone intending on adding a review for this? If not I propose merging and iterating via PR. |
I'd like to add a few of the edits you proposed, @alexkrolick, and then I'll go ahead and merge. Thanks for the review 😅 |
|
||
// This binding ensures that `this.renderTheCat` always refers | ||
// to the *same* function when we use it in render. | ||
this.renderTheCat = this.renderTheCat.bind(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we often see the usage of arrow functions instead of binding in the constructor.
Is there any particular reason not to use an arrow function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. The whole point of this example is that we create the function only once instead of dynamically creating a function on every render. Unless you're suggesting I use an arrow function here in the constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I meant Instead of this:
class MouseTracker extends React.Component {
constructor(props) {
super(props);
// This binding ensures that `this.renderTheCat` always refers
// to the *same* function when we use it in render.
this.renderTheCat = this.renderTheCat.bind(this);
}
renderTheCat(mouse) {
return <Cat mouse={mouse} />
}
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse render={this.renderTheCat} />
</div>
);
}
}
Maybe this makes more sense/more consistent?
class MouseTracker extends React.Component {
// `this.renderTheCat` always refers to the *same* function when we use it in render.
renderTheCat = (mouse) => {
return <Cat mouse={mouse} />
}
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse render={this.renderTheCat} />
</div>
);
}
}
Less lines, less confusing for newcomers, same effect (creating a single function instance)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs intentionally try to stick to standard syntax instead of proposal-stage. Class properties are getting there but aren't standard just yet 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alexkrolick my bad. I wasn't aware it isn't standard yet (with babel, the difference between standard and non standard is blurry). Thanks for the clarification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW @ron23 I always write my code in the way you're suggesting here and never actually write out my constructor
s, so I feel you. But ya, the React docs tend to favor .bind
at present.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// This binding ensures that
this.renderTheCat
always refers
// to the same function when we use it in render.
Is there any case that this.renderTheCat
refers a different function in render without this?
I know that this
in the function may not be a MouseTracker's instance, but I guess the reference to the function is always the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add "Using Props Other Than Render" to Render Props
* started translating 2016-04-07-react-v15 * translated introduction and installation guide sections * started to translate the changelog * finish the translation of React v15.0 * adhere to the official translation glossary * Remove extra "e" in content/blog/2016-04-07-react-v15.md Co-Authored-By: Jussara Soares <jussara.ac.s@hotmail.com> * Update content/blog/2016-04-07-react-v15.md to replace "mudanca" with "mudança" Co-Authored-By: Jussara Soares <jussara.ac.s@hotmail.com> * Update content/blog/2016-04-07-react-v15.md Co-Authored-By: Jussara Soares <jussara.ac.s@hotmail.com>
This PR is a re-submission of a PR that I had previously made to facebook/react but that was closed when the website moved to reactjs/reactjs.org. Sorry it took so long 😅
I'm planning on adding one more section that discusses the trade-offs when using a render prop with
React.PureComponent
, which was the source of some concern in the earlier submission./cc @thejameskyle who submitted #349 earlier today which discusses the same concept