react 右键
by Christoph Michel
克里斯托夫·米歇尔(Christoph Michel)
React uses the key
attribute during its reconciliation phase to decide which elements can be reused for the next render. They are important for dynamic lists. React will compare the keys of the new element with the previous keys and 1) mount components having a new key 2) unmount components whose keys are not used anymore.
在对帐阶段 ,React使用key
属性来决定哪些元素可用于下一个渲染。 它们对于动态列表很重要。 React会将新元素的键与先前的键进行比较,并且1)具有新键的安装组件2)不再使用其键的卸载组件。
Many React developers have heard the general advice that you should not use index
as a key. But what exactly can go wrong when using key
s in a bad way? What else can we do when we play around with keys?
许多React开发人员已经听到了一般性建议,即您不应使用index
作为键 。 但是,当以不好的方式使用key
时,究竟会出什么问题呢? 当我们玩弄钥匙时还能做什么?
For a better understanding, let’s consider the example of rendering a list of input
s. When clicking a button, we will insert a new item with text Front
to the front of the list.
为了更好地理解,让我们考虑呈现input
s列表的示例 。 单击一个按钮时,我们将在列表的前面插入一个文本为Front
的新项目。
import React from "react";import { render } from "react-dom";class Item extends React.PureComponent { state = { text: this.props.text }; onChange = event => { this.setState({ text: event.target.value }); }; componentDidMount() { console.log("Mounted ", this.props.text); } componentWillUnmount() { console.log("Unmounting ", this.props.text); } render() { console.log("rerendering ", this.props.text); const { text } = this.state; return ( <li> <input value={text} onChange={this.onChange} /> </li> ); }}class App extends React.Component { state = { items: [ { text: "First", id: 1 }, { text: "Second", id: 2 } ] }; addItem = () => { const items = [{ text: "Front", id: Date.now() }, ...this.state.items]; this.setState({ items }); }; render() { return ( <div> <ul> {this.state.items.map((item, index) => ( <Item {...item} key={index} /> ))} </ul> <button onClick={this.addItem}>Add Item</button> </div> ); }}render(<App />, document.getElementById("root"));
If you use index
as a key, the following happens:
如果使用index
作为键,则会发生以下情况:
CodeSandboxCodeSandbox is an online editor tailored for web applications.codesandbox.io
CodeSandbox CodeSandbox是为Web应用程序量身定制的在线编辑器。 codesandbox.io
What if another Item
with text Second
instead of Front
is inserted at the back of the list? Here's what happens:
如果在列表的后面插入了另一个文本为Second
而不是Front
Item
怎么办? 这是发生了什么:
Item is an uncontrolled component
: The text the user writes into its input
field is stored as state
Item is an uncontrolled component
:用户写入其input
字段的文本存储为state
A new data item { text: "Front" }
is inserted to the beginning of the list data.
新数据项{ text: "Front" }
插入到列表数据的开头。
The list is re-rendered with the index value as key
. So the previous components are re-used for the first two data items and are given the correct props Front
and First
, but the state is not updated in Item
. That's why the first two component instances keep the same text.
使用索引值作为key
重新呈现该列表。 因此,先前的组件被重新用于前两个数据项,并被赋予正确的道具Front
和First
,但状态未在Item
更新。 这就是为什么前两个组件实例保留相同文本的原因。
A new component instance is created for key: 2
because no previous matching key is found. It is filled with the props
of the last list data item which is Second
.
为key: 2
创建了新的组件实例,因为未找到先前的匹配键。 它填充了最后一个列表数据项的props
Second
。
Another interesting point is the render
calls that happen. Item is a PureComponent
, so it only updates when the text
prop (or state) changes:
另一个有趣的地方是发生的render
调用。 Item是PureComponent
,因此仅在text
PureComponent
(或状态)更改时才更新:
rerendering Frontrerendering Firstrerendering SecondMounted Second
All components are re-rendered. This happens because the element with key: 0
is reused for the first data item and receives its props
, but the first data item is now the new Front
object, triggering a render
. The same happens with the other components, because the old data items are now all shifted by one place.
所有组件都重新渲染。 发生这种情况的原因是, key: 0
的元素被第一个数据项重用并接收其props
,但是第一个数据项现在是新的Front
对象,从而触发了render
。 其他组件也会发生同样的情况,因为现在所有旧数据项都移位了一位。
So what’s the fix? The fix is easy: we give each list data item a unique id
one time upon creation (not on each render!). All components instances will be matched with their corresponding data item. They receive the same props
as before, and this avoids another render
.
那么解决方法是什么? 该修补程序很简单:我们独特的给每个列表数据项id
同一时间在创建时 (而不是在每个渲染!)。 所有组件实例将与其对应的数据项匹配。 他们收到与以前相同的props
,从而避免了其他render
。
Let’s ignore the performance benefits that come from using id
s in dynamic lists for now. The example shows that bugs introduced by keys only ever happen with regards to uncontrolled components, components that keep internal state.
现在让我们忽略在动态列表中使用id
带来的性能优势。 该示例表明, 键引入的错误只会发生在不受控制的组件 (保持内部状态的组件)方面。
If we rewrite Item
as a controlled component, by moving the state out of it, the bug is gone.
如果我们将Item
重写为受控组件,则通过将其移出状态,该错误将消失。
Why? Again, because the bug was reusing a component for a different data item. Therefore, the internal state still reflected the state of the previous data item, but the props of a different one. Making the component controlled, by removing its state completely, we don’t have this discrepancy anymore. (But there’s still the issue with the unnecessary re-renders.)
为什么? 再一次,因为该错误正在将组件重新用于其他数据项。 因此,内部状态仍然反映了前一个数据项的状态 ,但反映了另一个 数据项的道具 。 通过完全删除组件的状态来使组件受控制,我们不再存在这种差异。 (但是仍然存在不必要的重新渲染的问题。)
React only needs key
s when matching several elements, so setting a key on a single child is not needed. But it can still be useful to set a key on a single child component.
当匹配多个元素时,React仅需要key
,因此不需要在单个子代上设置键。 但是在单个子组件上设置键仍然有用。
If you change the key, React will throw away the whole component (unmount it), and mount a new component instance in its place. Why could this be useful?
如果您更改密钥,React将丢弃整个组件(将其卸载),并在其位置安装一个新的组件实例。 为什么这会有用?
Again, we’re coming back to uncontrolled components. Sometimes, you’re using a third-party component and you cannot modify its code to make it controlled. If a component has some internal state and it’s implemented in a bad way (for example, the state is derived only once in the constructor, but getDerivedStateFromProps
/ componentWillReceiveProps
is not implemented to reflect reoccurring props
changes in its internal state), the standard React toolbox cannot help you here. There is no forceRemount
.
再次,我们回到不受控制的组件 。 有时,您正在使用第三方组件,并且无法修改其代码以使其受到控制。 如果一个组件具有某种内部状态并且以不好的方式实现(例如,状态在构造函数中仅被导出一次 ,但是没有实现getDerivedStateFromProps
/ componentWillReceiveProps
来反映内部状态中 getDerivedStateFromProps
发生的props
更改) ,则标准React工具箱在这里无法帮助您。 没有forceRemount
。
However, we can just set a new key
on this component to achieve the desired behavior of completely initializing a new component. The old component will be unmounted, and a new one will be mounted with the new props
initializing the state
.
但是,我们可以在该组件上设置一个新key
,以实现完全初始化新组件所需的行为。 旧的组件将被卸载,并且一个新的将被安装的新props
初始化state
。
Using index
as a key can:
使用index
作为键可以:
introduce bugs when the list items are uncontrolled components but still use props
当列表项是不受控制的组件但仍使用props
时引入错误
The key
property can be used to force a complete remount of a component, which can sometimes be useful.
key
属性可用于强制完全重新安装组件,这有时很有用。
Originally published at cmichel.io
最初发布于cmichel.io
翻译自: https://www.freecodecamp.org/news/react-fun-with-keys-68f4c8c36f3e/
react 右键