首先 ,让我们回顾下JavaScript中的列表。

下面的例子里,我们使用函数map()来遍历一个列表的元素,并得到元素值得两倍。map()函数返回这个两倍于旧元素新的数组并打印log:

看看这两个组件:

  1. const numbers = [1, 2, 3, 4, 5];
  2. const doubled = numbers.map((number) => number * 2);
  3. console.log(doubled);

代码在控制台输出[2 , 4 , 6 , 8 , 10]。

在React中,将数组转换成元素列表几乎是相同的。

渲染多个组件

使用花括号{}来构造一个元素集合或把JSX写在里面。

下面 ,我们通过map()函数遍历一个number数组。让每个元素都返回自己的’

  • ‘。最终,我们把(一个
  • 的集合)结果赋给变量 listItems。
  1. const numbers = [1, 2, 3, 4, 5];
  2. const listItems = numbers.map((number) =>
  3. <li>{number}</li>
  4. );

我们用

  1. ReactDOM.render(
  2. <ul>{listItems}</ul>,
  3. document.getElementById('root')
  4. );

这段代码呈现了一个从1—5的列表。

基本列表元素

通常你需要渲染组件里面的列表。

我们重构先前的例子,使它接受一个数字数组参数的并且输出一个无序列表。

  1. function NumberList(props) {
  2. const numbers = props.numbers;
  3. const listItems = numbers.map((number) =>
  4. <li>{number}</li>
  5. );
  6. return (
  7. <ul>{listItems}</ul>
  8. );
  9. }
  10. const numbers = [1, 2, 3, 4, 5];
  11. ReactDOM.render(
  12. <NumberList numbers={numbers} />,
  13. document.getElementById('root')
  14. );

代码运行后,你发现有一个错误,说你需要给你的列表的元素一个Key(bundle.js:1289 Warning: Each child in an array or iterator should have a unique “key” prop.)。“key”是一个特殊的字符串属性,当你构造列表时,你需要提供这个属性。下一节中我们将讨论为什么这点如此重要。

让我们在numbers.map()中分配key给每个列表项来修复这个丢失了key的问题。

  1. function NumberList(props) {
  2. const numbers = props.numbers;
  3. const listItems = numbers.map((number) =>
  4. <li key={number.toString()}>
  5. {number}
  6. </li>
  7. );
  8. return (
  9. <ul>{listItems}</ul>
  10. );
  11. }
  12. const numbers = [1, 2, 3, 4, 5];
  13. ReactDOM.render(
  14. <NumberList numbers={numbers} />,
  15. document.getElementById('root')
  16. );

Keys

Key帮助React识别哪个元素发生了变更,是增加了?或者被移除了。数组中的元素应该被赋予一个具有稳定性的Key:

  1. const numbers = [1, 2, 3, 4, 5];
  2. const listItems = numbers.map((number) =>
  3. <li key={number.toString()}>
  4. {number}
  5. </li>
  6. );

挑出Key最好的方法是使用一个独立标识于其他兄弟元素的字符串。最常见的就是使用数据里面的ID作为Key:

  1. const todoItems = todos.map((todo) =>
  2. <li key={todo.id}>
  3. {todo.text}
  4. </li>
  5. );

万不得已时(当你没有一个稳定的ID来渲染元素时),你可能使用元素索引作为key。

  1. const todoItems = todos.map((todo, index) =>
  2. // Only do this if items have no stable IDs
  3. <li key={index}>
  4. {todo.text}
  5. </li>
  6. );

要是列表中的次序会改变,我们不推荐使用这种索引Key。这会造成性能问题还会导致关于组建状态的诸多问题。查看Robin Pokorny的论文 in-depth explanation on the negative impacts of using an index as a key.。如果你选择不是分配一个明确的key,那么儿啊春天就会默认使用索引作为key。

有兴趣了解更多,看看进一步解释为什么Key是必要的

根据Key提取组件

Key只在数组相关的上下文中生效。

举例说明,要是你提取组件:ListItem,你需要把key保持在身处数组中的元素中,而不是定义时的

  • 元素中。

例子:错误用法

  1. function ListItem(props) {
  2. const value = props.value;
  3. return (
  4. // Wrong! There is no need to specify the key here:
  5. <li key={value.toString()}>
  6. {value}
  7. </li>
  8. );
  9. }
  10. function NumberList(props) {
  11. const numbers = props.numbers;
  12. const listItems = numbers.map((number) =>
  13. // Wrong! The key should have been specified here:
  14. <ListItem value={number} />
  15. );
  16. return (
  17. <ul>
  18. {listItems}
  19. </ul>
  20. );
  21. }
  22. const numbers = [1, 2, 3, 4, 5];
  23. ReactDOM.render(
  24. <NumberList numbers={numbers} />,
  25. document.getElementById('root')
  26. );

例子:正确用法

  1. function ListItem(props) {
  2. // Correct! There is no need to specify the key here:
  3. return <li>{props.value}</li>;
  4. }
  5. function NumberList(props) {
  6. const numbers = props.numbers;
  7. const listItems = numbers.map((number) =>
  8. // Correct! Key should be specified inside the array.
  9. <ListItem key={number.toString()}
  10. value={number} />
  11. );
  12. return (
  13. <ul>
  14. {listItems}
  15. </ul>
  16. );
  17. }
  18. const numbers = [1, 2, 3, 4, 5];
  19. ReactDOM.render(
  20. <NumberList numbers={numbers} />,
  21. document.getElementById('root')
  22. );

一个好的经验是:元素在map()调用中使用key。

Key在兄弟元素中必须独一无二

Key的使用在一个数组中不能重复,可是,并不需要在全局中唯一。当我们构造了两个不一样的数组是,我们可以使用相同的key:

  1. function Blog(props) {
  2. const sidebar = (
  3. <ul>
  4. {props.posts.map((post) =>
  5. <li key={post.id}>
  6. {post.title}
  7. </li>
  8. )}
  9. </ul>
  10. );
  11. const content = props.posts.map((post) =>
  12. <div key={post.id}>
  13. <h3>{post.title}</h3>
  14. <p>{post.content}</p>
  15. </div>
  16. );
  17. return (
  18. <div>
  19. {sidebar}
  20. <hr />
  21. {content}
  22. </div>
  23. );
  24. }
  25. const posts = [
  26. {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
  27. {id: 2, title: 'Installation', content: 'You can install React from npm.'}
  28. ];
  29. ReactDOM.render(
  30. <Blog posts={posts} />,
  31. document.getElementById('root')
  32. );

Key的作用就像是给React暗示,但是它不能向下层组件传递。你要是需要相同的值,那你就另外一个prop名字,显式的把这个值传递。

  1. const content = posts.map((post) =>
  2. <Post
  3. key={post.id}
  4. id={post.id}
  5. title={post.title} />
  6. );

上述例子中,组件Post能读懂props.id,但是不能读懂props.key。

在map()中嵌入JSX

在上面的例子中,我们生命了一个ListItems变量并在里面写了JSX:

  1. function NumberList(props) {
  2. const numbers = props.numbers;
  3. const listItems = numbers.map((number) =>
  4. <ListItem key={number.toString()}
  5. value={number} />
  6. );
  7. return (
  8. <ul>
  9. {listItems}
  10. </ul>
  11. );
  12. }

JSX允许在任何花括号{}中嵌入表达式,所以我们也能把JSX内联在在map()的返回中:

  1. function NumberList(props) {
  2. const numbers = props.numbers;
  3. return (
  4. <ul>
  5. {numbers.map((number) =>
  6. <ListItem key={number.toString()}
  7. value={number} />
  8. )}
  9. </ul>
  10. );
  11. }

有时候这种写法清晰,有时候这种写法也让人迷惑。就像JavaScript,为了可读性而把它提取为变量,这完全决定于你。有一点要记住,要是map()中嵌套的太复杂,那么这将是一个提取成组件的好时机。

官网文章 Quick Start :Lists and Keys