我使用 ReactNative 为 iOS 和 android 构建了一个带有 ListView
的应用程序。使用有效数据源填充列表视图时,屏幕底部会打印以下警告:
警告:数组或迭代器中的每个孩子都应该有一个唯一的“key”道具。检查 ListView 的渲染方法。
这个警告的目的是什么?在消息之后他们链接到 this page,其中讨论了与 react native 无关的完全不同的事情,但与基于 web 的 reactjs 无关。
我的 ListView 是用这些语句构建的:
render() {
var store = this.props.store;
return (
<ListView
dataSource={this.state.dataSource}
renderHeader={this.renderHeader.bind(this)}
renderRow={this.renderDetailItem.bind(this)}
renderSeparator={this.renderSeparator.bind(this)}
style={styles.listView}
/>
);
}
我的数据源由以下内容组成:
var detailItems = [];
detailItems.push( new DetailItem('plain', store.address) );
detailItems.push( new DetailItem('map', '') );
if(store.telefon) {
detailItems.push( new DetailItem('contact', store.telefon, 'Anrufen', 'fontawesome|phone') );
}
if(store.email) {
detailItems.push( new DetailItem('contact', store.email, 'Email', 'fontawesome|envelope') );
}
detailItems.push( new DetailItem('moreInfo', '') );
this.setState({
dataSource: this.state.dataSource.cloneWithRows(detailItems)
});
ListView-Rows 使用以下内容呈现:
return (
<TouchableHighlight underlayColor='#dddddd'>
<View style={styles.infoRow}>
<Icon
name={item.icon}
size={30}
color='gray'
style={styles.contactIcon}
/>
<View style={{ flex: 1}}>
<Text style={styles.headline}>{item.headline}</Text>
<Text style={styles.details}>{item.text}</Text>
</View>
<View style={styles.separator}/>
</View>
</TouchableHighlight>
);
一切正常,如预期的那样,除了对我来说似乎完全是胡说八道的警告。
向我的“DetailItem”-Class 添加键属性并不能解决问题。
这就是“cloneWithRows”的结果真正将传递给 ListView 的内容:
_dataBlob:
I/ReactNativeJS( 1293): { s1:
I/ReactNativeJS( 1293): [ { key: 2,
I/ReactNativeJS( 1293): type: 'plain',
I/ReactNativeJS( 1293): text: 'xxxxxxxxxx',
I/ReactNativeJS( 1293): headline: '',
I/ReactNativeJS( 1293): icon: '' },
I/ReactNativeJS( 1293): { key: 3, type: 'map', text: '', headline: '', icon: '' },
I/ReactNativeJS( 1293): { key: 4,
I/ReactNativeJS( 1293): type: 'contact',
I/ReactNativeJS( 1293): text: '(xxxx) yyyyyy',
I/ReactNativeJS( 1293): headline: 'Anrufen',
I/ReactNativeJS( 1293): icon: 'fontawesome|phone' },
I/ReactNativeJS( 1293): { key: 5,
I/ReactNativeJS( 1293): type: 'contact',
I/ReactNativeJS( 1293): text: 'xxxxxxxxx@hotmail.com',
I/ReactNativeJS( 1293): headline: 'Email',
I/ReactNativeJS( 1293): icon: 'fontawesome|envelope' },
I/ReactNativeJS( 1293): { key: 6, type: 'moreInfo', text: '', headline: '', icon: '' } ] },
正如一个键所见,每条记录都有一个键属性。警告仍然存在。
DetailItem
很可能需要拥有密钥。如果它们已经具有唯一键,则需要显示其他渲染方法 (renderHeader, renderDetailItem, renderSeparator
)。在数据源以某种方式被修改(例如,行被删除)之前,它们工作得很好并且可以预期,此时当它们没有唯一标识符时,React 将不知道如何处理它们。
我和你有一段时间完全一样的问题,看了上面的一些建议后,我终于解决了这个问题。
事实证明(至少对我而言),我需要为从我的 renderSeparator 方法返回的组件提供一个密钥(一个名为“密钥”的道具)。向我的 renderRow 或 renderSectionHeader 添加一个键没有做任何事情,但是将它添加到 renderSeparator 使警告消失了。
希望有帮助。
您需要提供密钥。
如果您有关键属性,请尝试在您的 ListView Rows 中执行此操作:
<TouchableHighlight key={item.key} underlayColor='#dddddd'>
如果没有,请尝试将项目添加为键:
<TouchableHighlight key={item} underlayColor='#dddddd'>
您还可以使用迭代计数 (i) 作为 key
:
render() {
return (
<ol>
{this.props.results.map((result, i) => (
<li key={i}>{result.text}</li>
))}
</ol>
);
}
更改您的代码:
render() {
return (
<ol>
{this.props.results.map((result) => (
<li>{result.text}</li>
))}
</ol>
);
}
至:
render() {
return (
<ol>
{this.props.results.map((result) => (
<li key={result.id}>{result.text}</li>
))}
</ol>
);
}
然后解决了。
向列表的渲染根组件添加一个 prop 'key'。
<ScrollView>
<List>
{this.state.nationalities.map((prop, key) => {
return (
<ListItem key={key}>
<Text>{prop.name}</Text>
</ListItem>
);
})}
</List>
</ScrollView>
当您没有向列表项添加键时,会出现此警告。根据 react js Docs -
键帮助 React 识别哪些项目已更改、添加或删除。应为数组内的元素提供键,以使元素具有稳定的标识:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
选择键的最佳方法是使用一个字符串,该字符串在其兄弟项中唯一标识一个列表项。大多数情况下,您会使用数据中的 ID 作为键:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
当您没有用于渲染项目的稳定 ID 时,您可以使用项目索引作为键作为最后的手段
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
检查:key = undef !!!
您还收到警告消息:
Each child in a list should have a unique "key" prop.
如果您的代码完整正确,但如果打开
<MyComponent key={someValue} />
someValue 未定义!!!请先检查一下。您可以节省数小时。
如果您有一个空标记 <>
作为循环内结构的顶层,则会收到相同的错误:
return <select>
{Object.values(countries).map(c => {
return (<> {/* <== EMPTY TAG! */}
<option value={c.id}>{c.name}</option>
<States countryId={c.id} />
</>)
}
</select>
您可以使用 <React.Fragment>
的完整语法而不是短的 <>
并将您的密钥添加到完整标记中:
import {Fragment} from 'react';
return <select>
{Object.values(countries).map(c => {
return (<Fragment key={c.id}> {/* You can also use <React.Fragment> without import */}
<option value={c.id}>{c.name}</option>
<States countryId={c.id} />
</Fragment>)
}
</select>
我通过向 renderSeparator 组件添加属性来修复它,代码在这里:
_renderSeparator(sectionID,rowID){
return (
<View style={styles.separatorLine} key={"sectionID_"+sectionID+"_rowID_"+rowID}></View>
);
}
这个警告的关键词是“唯一”,sectionID + rowID 返回ListView中的唯一值。
假设 renderDetailItem 方法具有 following signature...
(rowData, sectionID, rowID, highlightRow)
尝试这样做...
<TouchableHighlight key={rowID} underlayColor='#dddddd'>
我用来解决这个问题的具体代码是:
renderSeparator(sectionID, rowID, adjacentRowHighlighted) {
return (
<View style={styles.separator} key={`${sectionID}-${rowID}`}/>
)
}
我包含了特定的代码,因为您需要密钥是唯一的——即使是分隔符也是如此。如果你做类似的事情,例如,如果你把它设置为一个常数,你只会得到另一个关于键重用的恼人的错误。如果您不了解 JSX,构建 JS 的回调来执行各个部分可能会非常痛苦。
在 ListView 上,显然附上了这个:
<ListView
style={styles.listview}
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
renderSeparator={this.renderSeparator.bind(this)}
renderSectionHeader={this.renderSectionHeader.bind(this)}/>
感谢 Coldbuffet 和 Nader Dabit,他们为我指明了这条道路。
这里是基于我的理解。希望它会有所帮助。它应该呈现任何组件的列表作为后面的示例。每个组件的根标记都需要有一个 key
。它不必是唯一的。不能是key=0
、key='0'
等。看来key没用了。
render() {
return [
(<div key={0}> div 0</div>),
(<div key={1}> div 2</div>),
(<table key={2}><tbody><tr><td> table </td></tr></tbody></table>),
(<form key={3}> form </form>),
];
}
似乎两个条件都满足了,也许 key('contact') 是问题
if(store.telefon) {
detailItems.push( new DetailItem('contact', store.telefon, 'Anrufen', 'fontawesome|phone') );
}
if(store.email) {
detailItems.push( new DetailItem('contact', store.email, 'Email', 'fontawesome|envelope') );
}
这一点怎么强调都不过分:
键仅在周围数组的上下文中才有意义。
“例如,如果您提取 ListItem 组件,则应将键保留在数组中的 <ListItem /> 元素上,而不是 ListItem 本身的 <li> 元素上。” -- https://reactjs.org/docs/lists-and-keys.html#extracting-components-with-keys
让我在这个问题上绊倒的事情是,我认为需要一个键应用于看起来像“真实”或 DOM HTML 元素,而不是我定义的 JSX 元素。
当然,对于 React,我们使用的是虚拟 DOM,因此我们定义的 React JSX 元素 <MyElement>
与看起来像真正的 DOM HTML 元素(如 <div>
)的元素一样重要。
那有意义吗?
key
并不意味着错误会消失。我准备放弃并用 CLI 工具替换我的 WIP Web 仪表板。
就我而言,我使用的是 Semantic UI React "Card" 视图。一旦我为我构建的每张卡片添加了一个密钥,警告就消失了,例如:
return (
<Card fluid key={'message-results-card'}>
...
</Card>
)
当您使用任何循环函数并在没有键的情况下渲染某些 HTML 元素时,即使您的父 div 有键,也会出现此错误,并且要解决此问题,您必须传递键。
请检查以下屏幕截图以更好地理解:
https://i.stack.imgur.com/hYZZU.png
我已按照上述方式修复了此警告。
这对我有用。
<View>
{
array.map((element, index) => {
return(
<React.Fragment key= {`arrayElement${index}`}>
{element}
</React.Fragment>
);
})
}
</View>
如果您将 <Fade in>
元素用于 React 应用程序,请在其中添加 key={}
属性,否则您会在控制台中看到错误。
SectionList
也是如此,必须为每个item
显式添加一个名为 key 的属性,以使 RN 满意。