[New Docs] Added: Lists & Conditional Rendering & Forms#7964
[New Docs] Added: Lists & Conditional Rendering & Forms#7964ericnakagawa merged 13 commits intofacebook:new-docsfrom ericnakagawa:new-docs
Conversation
|
|
||
| ### A Basic List | ||
|
|
||
| To render an array or collection of objects into a list use the ES6 `map()`. |
There was a problem hiding this comment.
Array.map was part of ES5, not ES6
|
|
||
| ### A Basic List Using Keyed Components | ||
|
|
||
| In this example we loop through a collection of objects, then pass props to a `Person` component. In our `map()` function we provide a second argument `index` that provides us with the current index of our loop. We use this index as our key, however more complex data benefits from a unique identifier (like a database object id). |
There was a problem hiding this comment.
It is slightly misleading to say that only complex data would benefit from unique identifiers as keys. If the order of the items could possibly change in any way, using the index as the key would be a bad idea.
There was a problem hiding this comment.
Yes. We should explain clearly that you must always supply keys. If items never reorder you may use their index. Otherwise use a string representing identity of the data item you are rendering. Mention that keys must only be unique among their siblings.
There was a problem hiding this comment.
I will rewrite this section to be clearer.
| ); | ||
| } | ||
| ``` | ||
| > Using a fat arrow function (`() => {}`) will automatically bind `this` to the function. |
There was a problem hiding this comment.
I think "arrow function" is more commonly used than "fat arrow function"
| <ul> | ||
| <li>John</li> /* key: "John" */ | ||
| <li>Fred</li> /* key: "Fred" */ | ||
| </ul> |
There was a problem hiding this comment.
Could you tie this back into the previous example with <Person/>? The markup is close to what that example would render, and I think it would be clearer.
In the <Person/> example you can just update the key to use person.name and update the text in the <li/> node here.
There was a problem hiding this comment.
Good idea. I've swapped around data per your suggestion.
| </ul> | ||
| ``` | ||
|
|
||
| As your components begin managing their own `this.state`, including keys becomes increasingly important to the performance of your application. |
There was a problem hiding this comment.
It's not clear how a component being stateful has any relation to keys.
There was a problem hiding this comment.
Will rewrite this section.
|
|
||
| In some cases, you will not want a component to render. To prevent components from rendering, return `null` or `false` from the `render()` function. | ||
|
|
||
| In the example below, a `<WarningBanner />` component will not be rendered within a `<Header /> component. |
There was a problem hiding this comment.
Missing back tick after <Header />
There was a problem hiding this comment.
Thanks for spotting. Fixed.
|
|
||
| ### Rendering Multiple Components Using {} | ||
|
|
||
| You can build collections of components and render them using the `{}` operator. |
There was a problem hiding this comment.
It might be useful to first explain that {} lets you inline any JavaScript statement within JSX. This kind of implies that {} is used specifically for rendering collections of components.
There was a problem hiding this comment.
Also I don't think {} is an operator. You can say "You can build collections of elements and include them in JSX with curly braces, just like you normally embed values in it." And you could link to "Introducing JSX" there.
|
|
||
| ```javascript | ||
| render() { | ||
| let persons = ['Ben', 'Chris', 'Dan', 'Paul', 'Tom']; |
There was a problem hiding this comment.
We went with using const for non changing data so let's do so consistently.
| <ul> | ||
| {persons.map((person) => { | ||
| return ( | ||
| <li>{person}</li> |
There was a problem hiding this comment.
This example, if ran as is, will print a warning about missing keys. Since this is the first section people might think that keys are optional. Can we either add keys here or mention that this example is incomplete and that they need to read the next section?
There was a problem hiding this comment.
I've redone the example to include a key from beginning.
|
|
||
| ### A Basic List Using Keyed Components | ||
|
|
||
| In this example we loop through a collection of objects, then pass props to a `Person` component. In our `map()` function we provide a second argument `index` that provides us with the current index of our loop. We use this index as our key, however more complex data benefits from a unique identifier (like a database object id). |
There was a problem hiding this comment.
Yes. We should explain clearly that you must always supply keys. If items never reorder you may use their index. Otherwise use a string representing identity of the data item you are rendering. Mention that keys must only be unique among their siblings.
|
|
||
| To render an array or collection of objects into a list use the ES6 `map()`. | ||
|
|
||
| ```javascript |
There was a problem hiding this comment.
Can we just use functional components here like in earlier guides? There is no use of state or lifecycle methods so I don't quite see why classes are necessary.
There was a problem hiding this comment.
Good call. I've rewrote as functional components to simplify.
| <ul> | ||
| {persons.map((person, index) => { | ||
| return ( | ||
| <Person key={index} name={person.name} color={person.color} /> |
There was a problem hiding this comment.
I think this is not a very good example because people's names are not unique. Maybe could use login or account ID?
There was a problem hiding this comment.
I removed this example.
| ### Keys | ||
|
|
||
| Use of a `key` can improve performance of your dynamic lists of child components by allowing reuse and reordering. | ||
|
|
There was a problem hiding this comment.
We shouldn't make it seem like keys are optional. React will complain loudly when you don't specify them. They are not just an optimization.
There was a problem hiding this comment.
I've include keys from first React example.
| When a `key` is provided, React chooses the most performant method for updating the rendered objects. | ||
|
|
||
| #### Example 1 | ||
|
|
There was a problem hiding this comment.
I feel like starting with HTML is confusing here. React users almost never have to deal with HTML. Maybe say "DOM structure"?
Also, I'm worried this looks like code the user needs to write. We previously used code snippets to show JSX, and suddenly we jump to HTML representation without a preceding JSX code example.
| ```html | ||
| <ul> | ||
| <li>Fred</li> /* key: "Fred" */ | ||
| <li style={display: 'none'}>Fred</li> /* key: removed */ |
There was a problem hiding this comment.
I don't understand this part. React doesn't hide DOM nodes. If they key is removed, so is the DOM node.
The only reason keys exist is to establish identity of the same element across renders to track re-ordering. There is no special handling of keys later.
| This follows the concept of "separation of concerns" by allowing each component to manage how it should be rendered. | ||
|
|
||
| ### Component Variables | ||
|
|
There was a problem hiding this comment.
I want to make separation between components and elements clear. I tried to do this in all existing guides so far.
Components are classes or functions you define. Elements are things like <Foo /> that describe what you want to see on the screen.
In this example variables store elements, not components.
There was a problem hiding this comment.
Going through now and updating terms -- hoping I got this right.
| LoginControl extends Component { | ||
| render() { | ||
| var loginButton; | ||
| if (loggedIn) { |
There was a problem hiding this comment.
Can you please make this a stateful component, read isLoggedIn from state, and toggle it on click? Otherwise it's not clear where that variable is coming from.
There was a problem hiding this comment.
Converted this and another examples to be stateful.
docs/_data/nav_docs.yml
Outdated
| title: Rendering Elements | ||
| - id: components-and-props | ||
| title: Components and Props | ||
| - id: lists-conditional-rendering |
There was a problem hiding this comment.
I think state should come before lists, but events should come before forms, so maybe interleave them:
state
lists
events
forms
?
There was a problem hiding this comment.
Makes sense, rearranging.
| this.login = this.login.bind(this); | ||
| this.logout = this.logout.bind(this); | ||
| } | ||
| login () { |
There was a problem hiding this comment.
this applies to a number of things through the code sample.
| login () { | ||
| this.setState({loggedIn: true,}); | ||
| } | ||
| logout () { |
There was a problem hiding this comment.
Fixing all fn calls
|
|
||
| ``` | ||
|
|
||
| <a target="_blank" href="https://codepen.io/ericnakagawa/pen/Egpdrz?editors=0010">Try it out on JSFiddle.</a> |
There was a problem hiding this comment.
you say JSFiddle but you mean CodePen
There was a problem hiding this comment.
Can we use consistent link format? I'm all for using for new tab but then we need to fix all other docs and make sure the link text is also the same.
There was a problem hiding this comment.
Can we use consistent link format? I'm all for using for new tab but then we need to fix all other docs and make sure the link text is also the same.
|
|
||
| Given the current state of your application you can assign elements as the value of variables, then only render the needed element. | ||
|
|
||
| In the example below we determine which button to display to a potential user. We track state using `this.state.loggedIn` to simulate whether or not they are logged in. If the user is logged in then we want to render a `<LogoutButton />`, otherwise we want `<LoginButton />`. We use a variable `loginButton` to hold either element depending on the value of `this.state.loggedIn` until we render. |
There was a problem hiding this comment.
Could you make it into two sentences instead of the , otherwise ? E.g.
"In the example below we determine which button to display to a potential user. We track state using this.state.loggedIn to represent whether or not they are logged in. If the user is logged in then we want to render a <LogoutButton />, otherwise we want a <LoginButton />. We use a variable loginButton to hold either element, depending on the value of this.state.loggedIn."
|
|
||
| When using prevening controls from being rendered a component must return only a single item. If you are returning multiple components, wrap them in `<div></div>`. | ||
|
|
||
| ### Rendering Multiple Components Using {} |
There was a problem hiding this comment.
This seems like the simplest content of all. Maybe this could go at the front, before using map?
There was a problem hiding this comment.
Moving this to top.
| ``` | ||
|
|
||
| <a target="_blank" href="https://codepen.io/ericnakagawa/pen/wzxEmv/?editors=0011">Try it out on Codepen.</a> | ||
|
|
There was a problem hiding this comment.
Sorry for duplicate comments. GitHub doesn't show my comments on older threads.
Can we use consistent link format? I'm all for using for new tab but then we need to fix all other docs and make sure the link text is also the same.
There was a problem hiding this comment.
Switched to normal links.
|
|
||
| First, let's talk about transforming lists in Javascript. | ||
|
|
||
| Given the code below, we use the `map()` function to take an array of `numbers`, double their values, and then output them to a new array `doubled`. |
There was a problem hiding this comment.
Can you please make map link to Array.map on MDN?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
There was a problem hiding this comment.
Removed link at end and moved link to map().
|
|
||
| ```javascript | ||
| const numbers = [1, 2, 3, 4, 5]; | ||
| numbers.map((number) => number * 2); // outputs: [2, 4, 6, 8 10] |
There was a problem hiding this comment.
It's not obvious that this returns an array but this is crucial to understanding.
Can we make it
const numbers = ...
const doubled = ...
Or similar?
There was a problem hiding this comment.
I had something similar prior. Adding back.
| numbers.map((number) => number * 2); // outputs: [2, 4, 6, 8 10] | ||
| ``` | ||
|
|
||
| In React, transforming arrays into lists of elements is nearly identical. |
There was a problem hiding this comment.
Let's make "elements" a link to "rendering elements"?
There was a problem hiding this comment.
This would really benefit from the same two-liner
const numbers = ...
const listItems = ...
There was a problem hiding this comment.
Applied second array in first 3 examples.
|
|
||
| A component must return only a single item. If you are returning multiple elements, wrap them in `<div></div>`. | ||
|
|
||
| Here we take our list of `numbers` and generate a collection of `<li>` elements. We are using the plain Javascript `map()` function. Learn more about it <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map">here</a>. |
There was a problem hiding this comment.
Oh, I see you link to map here. I would probably link in both places.
| Here we take our list of `numbers` and generate a collection of `<li>` elements. We are using the plain Javascript `map()` function. Learn more about it <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map">here</a>. | ||
|
|
||
| ```javascript | ||
| class Numbers extends React.Component { |
There was a problem hiding this comment.
Can you please write this as functional component instead? It doesn't use state or lifecycle so it doesn't need to be a class. You can find examples of functional components in "components and props" guide.
There was a problem hiding this comment.
The Codepen is written in a Functional Component, this example wasn't updated properly. Fixed.
| return ( | ||
| <div> | ||
| {warningBanner} | ||
| <button onClick={this.toggleWarning}>{this.state.toggleText}</button> |
There was a problem hiding this comment.
Can you please split this into three lines for readability?
|
|
||
| ### Rendering Multiple Components Using {} | ||
|
|
||
| You can build collections of components and include them in <a target="_blank" href="/react/docs/introducing-jsx.html">JSX</a> with curly braces `{}` just as you would embed values with Javascript. |
There was a problem hiding this comment.
"collections of elements", not components.
"Rendering Multiple Components" is fine because you get components as a result, but whenever we talk about manipulating JSX we always talk about the elements.
|
|
||
| <a target="_blank" href="https://codepen.io/ericnakagawa/pen/ozragV?editors=0011#0">Try it out on Codepen.</a> | ||
|
|
||
| ### Rendering Multiple Components Using {} |
There was a problem hiding this comment.
Can we skip "using {}" in the title?
|
|
||
| function Number(props) { | ||
| let numbers = []; | ||
| numbers.push(<Number value="1" />) |
There was a problem hiding this comment.
This example will print React key warnings. It also gives a false impression that you would often write render this way (in practice I almost never saw push() usage in render).
Another confusing thing about it is it seems like a "special case" because it is separated from other sections but it is showing exact same technique as we used before: embedding arrays of elements.
Instead of a separate section I would make it a first step in the "loops" section.
First loops should show
const numbers = ...
const doubled = ...
The it should show
const numbers = ...
const listItems = numbers.map(...)
ReactDOM.render(<ul>{listItems}</ul>, ...)
Then it should show component:
function NumberList(props) {
const numbers = props.numbers
const listItems = numbers.map(...)
return <ul>{listItems}</ul>
}
const numbers = ...
ReactDOM.render(<NumberList numbers={...}, ...)
And only then show the "inline" notation
function NumberList(props) {
const numbers = props.numbers
return (
<ul>
{numbers.map(...)}
</ul>
)
}
This way we do exactly single step every time, and it's easy to keep track of what's changing and at the same time understand that it's just JavaScript.
As final step you could extract a Number component and use that to demonstrate key stays inside map.
There was a problem hiding this comment.
Moved Multiple Component example to top, and followed your guide for introducing each level.
| var loggedIn = false; | ||
| return ( | ||
| <div> | ||
| The user is <strong>{(loggedIn)?'currently':'not'}</strong> logged in. |
There was a problem hiding this comment.
Please remove extra parens and add whitespace before and after ? and :
gaearon
left a comment
There was a problem hiding this comment.
Overall I'm really happy with how it's going. Leaving a few comments
| ```javascript{11} | ||
| function NumberList(props) { | ||
| const numbers = props.numbers | ||
| const listItems = numbers.map((item) => <li key={"item-" + item}>{item}</li>); |
There was a problem hiding this comment.
As soon as one-liners get a little crowdy it's best to add some whitespace
stuff = numbers.map((item) =>
<li stuff>
{moreStuff}
</li>
)
There was a problem hiding this comment.
Adding white space to these.
| } | ||
|
|
||
| const numbers = [1, 2, 3, 4, 5]; | ||
| ReactDOM.render(<NumberList numbers={numbers} />, document.getElementById('root')); |
There was a problem hiding this comment.
Same here, could
render(
<Stuff />,
stuff
);
There was a problem hiding this comment.
breaking this apart, too.
|
|
||
| Use keys that represent the identity of the item. If you don't have a way to uniquely identify each item consider using a string representation of the item. _You will be warned if you don't include keys._ | ||
|
|
||
| Supply keys to elements inside a `map()` function and not directly to an HTML element. Keys used within groups should be unique to each other. Keys cannot be accessed as a `prop`. |
There was a problem hiding this comment.
The problem is not that it's "directly to an HTML element". (This could actually be a component element too and the rule would still apply.)
Maybe say "Keys should be given to the elements inside the array because they give the elements a stable identity. If you extract a Number component, you should keep the key on the <Number /> elements in the array rather than on the root <li> element in the Number itself."
There was a problem hiding this comment.
"Keys cannot be accessed as a prop" => "This is why keys are not props. They serve as a hint to React but they don't get passed to your components. If you need the same value in your component, pass it explicitly as a prop with a different name."
Also this section is not essential and could be mentioned below.
| this.login = this.login.bind(this); | ||
| this.logout = this.logout.bind(this); | ||
| } | ||
| login() { |
There was a problem hiding this comment.
Maybe call them handleLoginClick and handleLogoutClick to match the most common convention
| [Try it out on Codepen.](https://codepen.io/ericnakagawa/pen/Egpdrz?editors=0010) | ||
|
|
||
|
|
||
| ### Prevent Component Rendering |
|
|
||
| To prevent components from rendering, return `null` or `false` from the `render()` function. | ||
|
|
||
| In the example below, the `<WarningBanner/>` is rendered depending on the value of the prop `warn`. If the value of the prop is false, then the component does not render. |
There was a problem hiding this comment.
Nit: space before self closing tag slash
|
|
||
| ### Prevent Component Rendering | ||
|
|
||
| To prevent components from rendering, return `null` or `false` from the `render()` function. |
There was a problem hiding this comment.
Maybe start as "A component may also prevent its own rendering."
It's not quite clear when you'd use each pattern so I'm not sure if it's worth diving into this here at all. But it's good to have an example somewhere so might as well be here.
But I think a ternary should be explained first as it's a more common pattern. Or at least you should explain {someCondition && <Stuff />} first. It is way more common that parent decides which children to render, than a component decides to not render itself.
There was a problem hiding this comment.
Moved ternary up.
There was a problem hiding this comment.
I added an example to Conditional Rendering.
gaearon
left a comment
There was a problem hiding this comment.
Accepting with nits. Please keep the style consistent with all other code examples. We keep semicolons, try to split lines when they're too crowdy, use const and let over var.
| <li> | ||
| {item} | ||
| </li> | ||
| ) |
| <li> | ||
| {item} | ||
| </li> | ||
| ) |
| {item} | ||
| </li> | ||
| ) | ||
| return <ul>{listItems}</ul> |
| <li key={"item-" + item}> | ||
| {item} | ||
| </li> | ||
| ) |
| ```javascript{11} | ||
| ```javascript{4} | ||
| function NumberList(props) { | ||
| const numbers = props.numbers |
| return <div>Please sign up.</div> | ||
| } | ||
| function VisitorMessage(props) { | ||
| var loggedIn = true; // change this to false |
There was a problem hiding this comment.
Please no var, only const or let.
| ``` | ||
|
|
||
| [Try it out on Codepen](https://codepen.io/ericnakagawa/pen/WGaKLy?editors=0011#0) | ||
|
|
There was a problem hiding this comment.
Note that && is regular JS operator, and this works because React ignores false/null in render output.
| var loggedIn = false; | ||
| return ( | ||
| <div> | ||
| The user is <strong>{loggedIn?'currently':'not'}</strong> logged in. |
There was a problem hiding this comment.
Please put spaces before and after ? and :
| } | ||
| ``` | ||
|
|
||
| ### Prevent Component Rendering |
There was a problem hiding this comment.
"Preventing" for title, not "Prevent", as it's a noun
| In the example below, the `<WarningBanner />` is rendered depending on the value of the prop `warn`. If the value of the prop is false, then the component does not render. | ||
|
|
||
| The highlighted lines below show where the component `<WarningBanner />` returns null and the component isn't rendered. | ||
|
|
There was a problem hiding this comment.
Please don't use this style:
if(!props.warn) return null;Instead:
if (!props.warn) {
return null;
}
No description provided.