此文章是翻译Lists and Keys这篇React(版本v16.2.0)官方文档。
Lists and Keys
首先,让我们复习一下在JavaScript 中如何改变list。
给定代码如下,我们使用map()函数来接受一个numbers 数组并对其值进行加倍。我们将map()函数的返回值赋给doubled 并打印这个值:
const numbers = [1, 2, 3, 4, 5]const doubled = numbers.map((number)=> number*2)console.log(doubled)
代码输出值为[2, 4, 6, 8, 10]。
在React 中,改变元素 数组的值几乎是一样的。
Rendering Multiple Components
你可以构建元素集合,在JSX 中(include them in JSX)使用大括号{} 来包含它们。
下面,我们通过JavaScript map()方法来循环遍历一个numbers 数组。为数组每一项返回一个<li> 元素。最后,我们将元素数组结果赋值给listItem:
const numbers = [1, 2, 3, 4, 5]const listItem = numbers.map((number)=><li>{number}</li>);
我们将其全部listItems 数组包裹在<ul> 元素中,然后将其渲染到DOM 上):
ReactDOM.render(<ul>{listItem}</ul>,document.getElementById('root'));
这段代码展示从1 到5 的带着重号的数字列表
Basic List Component
通常你会在一个组件)中渲染一个列表。
我们重构之前的例子为组件,此组件接受一个numbers 数组,输出也一个元素的无序列表。
function NumberList(props) {const numbers = props.numbers;const listItems = numbers.map((number)=><li>{number}</li>);return (<ul>{listItems}</ul>);}const numbers = [1, 2, 3, 4, 5];ReactDOM.render(<NumberList numbers={numbers} />,document.getElementById('root'));
当你运行这段代码,会有一个警告提示,其需要一个key。一个”key” 作为一个重要的特性在创建的元素。在下一节,我们来讨论为什么它非常重要。
让我们在numbers.map() 中赋予一个key 来解决缺失key 问题。
function NumberList(props) {const numbers = props.numbers;const listItems = numbers.map((number)=><li key={number.toString()}>{number}</li>);return (<ul>{listItems}</ul>);}const numbers = [1, 2, 3, 4, 5];ReactDOM.render(<NumberList numbers={numbers} />,document.getElementById('root'));
Keys
Keys 帮助React 确认列表中哪一项发生改变,被添加或者是被移除。在数组中需要给元素一个稳定的身份(identify):
const numbers = [1, 2, 3, 4, 5];const listItem = numbers.map((number)=><li key={number.toString()}>{number}</li>);
最好的提供一个唯一标志的字符串在其兄弟节点中。大多数情况下你使用数据中的IDs 作为key:
const todoItemss = todos.map((todo)=><li key={todo.id}>{todo.text}</li>);
当你不需要为渲染列表提供IDs 时,你可以使用列表索引作为最后的手段(a last resort):
const todoItemss = todos.map((todo, index)=>// Only do this if items have no stable IDs<li key={index}>{todo.text}</li>);
如果数据能够被重新排列,我们不建议使用索引作为keys。这会对性能产生负面影响,并可能导致组件状态问题。查看Robin Pokorny 的文章in-depth explanation on the negative impacts of using an index as a key。如果你选择不给列表条目赋值一个名曲的key,那么React 将默认使用索引作为key。
这有一篇文章in-depth explanition about why keys are necessary,如果你有兴趣了解更多。
Extracting Components with Keys
Keys 只有在数组的上下文中才有意义。
例如,如果你提取了一个ListItem 组件,你应该在数组的<ListItem /> 元素上使用这个key,而不是在ListItem 自身根的<li> 元素上。
错误的使用Key 的例子:
function ListItem(props) {const value = props.value;return (// Wrong! There is no need to specify hte key here:<li key={value.toString()}>{value}</li>);}function NumberList(props) {const numbers = props.numbers;const listItems = numbers.map((number)=>// Wrong! The key should have been specified here:<ListItem value={number}/>);return(<ul>{listItems}</ul>);}const numbers = [1, 2, 3, 4, 5];ReactDOM.render(<NumberList numbers={numbers} />,document.getElementById('root'));
正确使用Key 的例子:
function ListItem(props) {const value = props.valuereturn (// Correct! There is no need to specify the key here:<li>{value}</li>)}function NumberList(props) {const numbers = props.numbers;const listItems = numbers.map((number)=>// Correct! Key should be specified inside the array<ListItem key={number.toString()}value={number}/>);return(<ul>{listItems}</ul>);}const numbers = [1, 2, 3, 4, 5];ReactDOM.render(<NumberList numbers={numbers} />,document.getElementById('root'));
一个好方法是map()调用中的元素需要使用keys。
Keys Must Only Be Unique Among Sibings
在数组中Keys 的使用必须是在它们兄弟中唯一的。即时它们不是全局唯一。我们可以使用相同的keys 在不同的数组中:
function Blog(props) {const sidebar = (<ul>{props.posts.map((post)=><li key={post.id}>{post.title}</li>)}</ul>);const content = props.posts.map((post)=><div key={post.id}><h3>{post.title}</h3><p>{post.content}</p></div>);return (<div>{sidebar}<hr />{content}</div>);}const posts = [{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},{id: 2, title: 'Installation', content: 'You can install React from npm.'}];ReactDOM.render(<Blog posts={posts} />,document.getElementById('root'));
Keys 只是作为React 的线索它们不能传递给你的组件。如果你必须在你的组件中使用同样的值,请明确将其传给一个不同名字的prop:
const content = posts.map((post)=><Postkey={post.id}id={post.id}title={post.title}/>);
在上述例子中,这个Post 组件只能读取props.id 而不能读取props.key
Embedding map() in JSX
在上述例子中我们声明了一个不同listItems 变量在JSX 中。
function NumberList(props) {const numbers = props.numbers;const listItems = numbers.map((number)=><ListItem key={number.toString()}value={number}/>);return(<ul>{listItems}</ul>);}
JSX 允许在大括号里嵌入任何表达式embedding any expression,所以我们可以行内map() 结果。
function NumberList(props) {const numbers = props.numbersreturn (<ul>{numbers.map((number)=><ListItem key={number.toString()}value={number} />)}</ul>)}
有时在这清晰代码中结果,但是这种样式可能被滥用。像在JavaScript 中,它只是由你来决定提取一个变量是否更可读。记住如果map() 体中太嵌套了,提取一个组件)是更好的方法。
