纯组件中的反应本机更新FlatList数据
我对Reaction Native有些陌生,并且一直在使用FlatList时遇到这个问题,我想更新它使用的数据源,并让它更新它自己。我的案例是有一个我想用FlatList显示的项目列表,然后每个项目都是可选择的。我看到的大多数示例都是使用组件,建议已经将state
添加到extraData
来解决这个问题。然而,我使用的是纯组件,并尝试查看在这种情况下是否有任何解决方案。遵循我创建的一个样例项目。在此示例中,发生了按下操作,data
被更新,但FlatList不反映data
更改。
import { findIndex } from 'lodash'
import React, { useState } from 'react'
import {
SafeAreaView,
StyleSheet,
View,
Text,
StatusBar,
FlatList,
Pressable,
} from 'react-native'
import {
Colors,
} from 'react-native/Libraries/NewAppScreen'
var array = require('lodash/array')
const App = () => {
const [data, setData] = useState([
{
name: "Foo",
id: '1',
isSelected: false
},
{
name: "Boo",
id: '2',
isSelected: false
},
{
name: "Koo",
id: '3',
isSelected: false
},
{
name: "Poo",
id: '4',
isSelected: false
},
{
name: "Too",
id: '5',
isSelected: false
},
{
name: "Qoo",
id: '6',
isSelected: false
},
])
const tappedItem = (item) => {
const itemIndex = data.findIndex((i) => i.id == item.id)
const newItem = data[itemIndex]
newItem.isSelected = !newItem.isSelected
data[itemIndex] = newItem
setData(data)
}
const renderItem = (item) => {
return (
<Pressable onPress={() => tappedItem(item.item)}>
<View style={[styles.sectionContainer, { backgroundColor: item.item.isSelected ? 'pink' : 'lightgray' }]}>
<Text style={styles.sectionTitle}>{item.item.name}</Text>
</View>
</Pressable>
)
}
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<View style={styles.body}>
<FlatList
data={data}
keyExtractor={item => item.id}
renderItem={renderItem}
extraData={data}
/>
</View>
</SafeAreaView>
</>
)
}
const styles = StyleSheet.create({
body: {
backgroundColor: Colors.white,
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: Colors.black,
},
})
export default App
解决方案
最终输出:
如何实现FlatList项的选择和取消选择:
import React, { useState } from 'react';
import {
SafeAreaView,
StyleSheet,
View,
Text,
StatusBar,
FlatList,
Pressable,
} from 'react-native';
const App = () => {
const [data, setData] = useState([
{
name: 'Foo',
id: '1',
isSelected: false,
},
{
name: 'Boo',
id: '2',
isSelected: false,
},
{
name: 'Koo',
id: '3',
isSelected: false,
},
{
name: 'Poo',
id: '4',
isSelected: false,
},
{
name: 'Too',
id: '5',
isSelected: false,
},
{
name: 'Qoo',
id: '6',
isSelected: false,
},
]);
const tappedItem = (item) => {
console.log(item);
const modifiedList= data.map((element) => {
if (element.id === item.id) {
return { ...element, isSelected: !element.isSelected };
}
return element;
});
setData(modifiedList);
};
const renderItem = (item) => {
return (
<Pressable onPress={() => tappedItem(item.item)}>
<View
style={[
styles.sectionContainer,
{ backgroundColor: item.item.isSelected ? 'pink' : 'lightgray' },
]}>
<Text style={styles.sectionTitle}>{item.item.name}</Text>
</View>
</Pressable>
);
};
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<View style={styles.body}>
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={renderItem}
extraData={data}
/>
</View>
</SafeAreaView>
</>
);
};
const styles = StyleSheet.create({
body: {
backgroundColor: 'white',
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: 'black',
},
});
export default App;
工作tappedItem()
:
/*
Lets assume we pressed first item in the FlatList, the item that we will be passing
is: {
name: 'Foo',
id: '1',
isSelected: false,
}
After that is done most important and also probably the easiest part,
the modification of the existing state, i.e. toggling the selected property.
*/
const tappedItem = (item) => {
console.log(item);
/*
In the following map operation, we iterate through the existing state, i.e. data.
and when we find the matching id, 1 in the case of
=> {
name: 'Foo',
id: '1',
isSelected: false,
}
we return the mutated object where we change only the `isSelected` property
You can see this happening here:
=> if (element.id === item.id) {
return { ...element, isSelected: !element.isSelected };
}
in above if statement, we spread the `element` object, toggle the `isSelected` property and return it.
If the id does not matches then we can simply return the original `element` object without any changes.
So after that, we will get the modifiedList which will have the `isSelected` toggled as we intend to.
Then we simply set the data state with that of the newly modified list.
=> setData(modifiedList);
*/
const modifiedList= data.map((element) => {
if (element.id === item.id) {
return { ...element, isSelected: !element.isSelected };
}
return element;
});
setData(modifiedList);
};
/*
this variation of your existing tappedItem function will work fine too,
just be careful not to mutate the states directly as you did in the original case
=> data[itemIndex] = newItem [❌]
=> let temp = [...data]
temp[itemIndex] = newItem; [✔]
*/
const tappedItem = (item) => {
let temp = [...data] //<= create a copy of current state
const itemIndex = data.findIndex((i) => i.id == item.id);
const newItem = data[itemIndex];
newItem.isSelected = !newItem.isSelected;
temp[itemIndex] = newItem; //<= modify that copy
setData(temp); //<= use it to set the state.
};
完整工作演示:Expo Snack
相关文章