Warning: Failed child context type: Invalid child context `virtualizedCell.cellKey` of type `number` supplied to `CellRenderer`, expected `string`.
ravirajn22 opened this issue ยท 15 comments
Getting the below warning when I use FlatList and where keyExtractor returns a number.
Warning: Failed child context type: Invalid child context `virtualizedCell.cellKey` of type `number` supplied to `CellRenderer`, expected `string`.
in CellRenderer (created by VirtualizedList)
in RCTScrollContentView (created by ScrollView)
in RCTScrollView (created by ScrollView)
in ScrollView (created by VirtualizedList)
in VirtualizedList (created by FlatList)
Sample Code for reproduction
import React, { Component } from 'react';
import { Text, View, FlatList } from 'react-native';
export default class NotificationSettingsScreen extends Component {
render() {
return (
<View style={{flex: 1, paddingTop: 36}}>
<FlatList
data={[1, 2, 3, 4, 5]}
renderItem={({item}) => (
<View>
<Text>{item}</Text>
</View>
)}
keyExtractor={(item) => item }
/>
</View>
);
}
}
NOTE: Changing keyExtractor={(item) => item.toString()}
makes this warning disappear.
Environment
React-Native: 0.53.0,
react: 16.2.0
Expected Behavior
keyExtractor of FlatList must accept number instead of just string
Actual Behavior
Warning when using keyExtractor which returns number
Steps to Reproduce
Check above lines
The above problem could also be due to this commit a010a0c but not sure ...
Thanks for posting this! It looks like your issue may refer to an older version of React Native. Can you reproduce the issue on the latest stable release?
Thank you for your contributions.
The prop expects a string but you're setting a number. This seems to be working as expected.
Ya I got it , that it requires a string. Wanted to just confirm was it some minor regression or planned change because many use index as keys which will be number. Also I could not find this change in release notes. But anyways thanks for the clarity .
@ravirajn22 Using index as the key is bad practice. You can use `keyExtractor = (item, index) => 'list-item-${index}'.
What's the rationale behind this change? Many apps use an unique number id (eg. user id, item id) as the key. Why require it to be cast to a string?
"Using index as the key is bad practice."
Which pattern or practice is that violating?
keyExtractor = { (item, index) => index.toString() };
The value of keyExtractor must be a stringใ
@peacechen I think this is one of the reasons. I don't know what the specifics are but, even though it's an article about React, I wouldn't be surprised if it's a problem on React Native too.
The way I understand it, keys help with identifying objects throughout multiple renders, so if you remove and re-add an object to the list, it should recognize it by its key. It might also be a way to improve performance by finding the cached item and rendering it instead of creating a new one. Having an index (0,1,2,3,etc.) defeats this purpose. The AirBnB style guide also discourages the use of index as keys by quoting this article.
@PatriceVignola Thanks for the article. It's not convincing though. While strings may offer the potential for better uniqueness, there's no guarantee that every single string key will be unique within the app. In my app there are multiple FlatLists, and within them there can instances of component A that end up with the same string key between those FlatLists.
Ok, so let's assume that the keys are isolated between each FlatList. If that is true, then integer index keys shouldn't be an issue as far as uniqueness.
If each entry's key must be globally unique, no matter where it lives, then React/RN relies on app developers to generate a unique random number (aka hash) that has little or nothing to do with the instance. If that really is the case, then React/RN might as well just internally generate a unique hash for each item in the FlatList, if that is so critical to React rendering.
Why we can use index as key in SectionList
?
keyExtractor = { (item, index) => index.toString() };
The value of keyExtractor must be a stringใ
It works well.
"Using index as the key is bad practice."
Which pattern or practice is that violating?
Its not really a bad practice. Its totally wrong.
When you remove an item from the array the indexes might change and the List component no longer works as intended. The List component will reuse a component that just left the screen and will use that component for the new item that appeared on the other side of the screen. This is why the key must not change in run-time. For this reason, using the index of the array is wrong.
Edit: This is just a hunch. I'm not 100% sure how the ListItem recycling works.
We ran into this error in an App for a client using <FlatList>
today. Very grateful this thread was here or we would have been totally lost.
Its not really a bad practice. Its totally wrong.
When you remove an item from the array the indexes might change and the List component no longer works as intended. The List component will reuse a component that just left the screen and will use that component for the new item that appeared on the other side of the screen. This is why the key must not change in run-time. For this reason, using the index of the array is wrong.
Edit: This is just a hunch. I'm not 100% sure how the ListItem recycling works.
This is less about whether keys should be unique, but rather why this task was delegated to app developers. Since the key is crucial for row recycling, FlatList should handle key assignments internally with a GUID or similar unique hash. Having apps do this is just inviting trouble. I've not found the keys to be useful for the app itself (such as identifying particular rows during runtime; it's better to use the row data for identification), so there shouldn't be much concern if the keys are opaque. Also, looking at other parts of React, there are internally generated unique identifiers for components. Put a breakpoint in your app and poke around at the instantiated components.
It would be helpful for the FlatList author to chime in here.
keyExtractor = { (item, index) => index.toString() };
this woked very well