[英]React Native State Updated when it should not
I have filterData
which comes from a controller, so i set filterData
to options, for the UI, Like this我有来自
filterData
的 filterData,所以我将filterData
设置为选项,对于 UI,像这样
const [state, setState] = React.useState({
options: filterData,
});
filter data is this过滤数据是这个
[{"List": [{"Name": "TEST1", "isSelected": false}, {"Name": "TEST2", "isSelected": false}, {"Name": "TEST3", "isSelected": true}], "Type": "Type"},
{"List": [{"Name": "TEST4", "isSelected": false}, {"Name": "TEST5", "isSelected": false}, {"Name": "TEST6", "isSelected": true}], "Type": "Type2"}]
I render it in a flatlist
and then map
the List, this is the code for my flatlist
我将它呈现在一个
map
flatlist
,这是我的flatlist
列表的代码
<FlatList
style={{flex: 1, backgroundColor: colors.white}}
data={state.options}
keyExtractor={(_, index) => index.toString()}
renderItem={({item, index}) => {
return (
<View style={{marginBottom: widthPercentageToDP(5)}}>
<Text>
{item.Type}
</Text>
<View
style={{
flexDirection: 'row',
flexWrap: 'wrap',
}}>
<View
style={{
marginRight: 8,
marginBottom: 8,
}}>
<TouchableOpacity>
<Text>
Select All
</Text>
</TouchableOpacity>
</View>
{item.List.map((e: any, idx: number) => (
<View
key={idx}>
<TouchableOpacity
onPress={() =>
handleSelect(idx, item.Type)
}
>
<Text>
{e.Name}
</Text>
</TouchableOpacity>
</View>
))}
</View>
</View>
);
}}
</View>
</View>
);
}}
/>
as you can see from above, i will have some buttons
.正如你从上面看到的,我会有一些
buttons
。 And if the button
is clicked, it will call handleSelect
function.如果单击该
button
,它将调用handleSelect
function。 which is this这是这个
const handleSelect = async (idx: number, type: string) => {
let arr = [...state.options];
arr.map((item) => {
if (item.Type === type) {
item.List[idx].isSelected = !item.List[idx].isSelected;
}
});
console.log(state.options)
};
i didnt change the state of the options like the code below, only console.log我没有更改选项的 state,如下面的代码,只有 console.log
setState((prevState) => ({
...prevState,
options: arr,
}));
The problem is, in the log when i clicked and run that function, state.options
will also change.问题是,当我单击并运行日志时,function、
state.options
也会发生变化。 it will have the same data as arr
.它将具有与
arr
相同的数据。 Even filterData
also change when i console.log
in handleselect
.当我
console.log
in handleselect
时,甚至filterData
也会发生变化。 I already checked my controller and it didnt trigger any function.我已经检查了我的 controller,它没有触发任何 function。 Does anyone know why it update all of my variable?
有谁知道为什么它会更新我的所有变量?
When you create a copied array like let arr = [...state.options];
当您创建一个复制的数组时,例如
let arr = [...state.options];
, the variable arr
is a new array, but the elements of that array are references to the same objects as in the original state.options
. ,变量
arr
是一个新数组,但该数组的元素是对与原始state.options
中相同的对象的引用。 So when you toggle isSelected
on an element of arr
, you are also changing the value in your state.因此,当您在
arr
的元素上切换isSelected
时,您也在更改 state 中的值。
This line is the where the mutation occurs:这一行是突变发生的地方:
item.List[idx].isSelected = !item.List[idx].isSelected;
Your arr.map()
doesn't actually return anything.你的
arr.map()
实际上并没有返回任何东西。 .map()
is meant to map an element to a new value so your callback needs to return the new value. .map()
旨在将 map 元素设置为新值,因此您的回调需要返回新值。 You probably meant to use .forEach()
.您可能打算使用
.forEach()
。 We actually do want .map()
though, but we need to return the item
.虽然我们确实想要
.map()
,但我们需要返回item
。
As a sidenote, you can have multiple useState
hooks per component so you don't need to use object properties like you would in a class component.作为旁注,每个组件可以有多个
useState
挂钩,因此您不需要像在 class 组件中那样使用 object 属性。 What you have is not a problem, but you can make it simpler:你所拥有的不是问题,但你可以让它更简单:
const [options, setOptions] = React.useState(filterData);
You need to create a copied object for any objects where you are changing properties.您需要为要更改属性的任何对象创建一个复制的 object。 You don't actually need to copy the whole array because
.map
already returns a copy.您实际上不需要复制整个数组,因为
.map
已经返回了一个副本。
const handleSelect = async (idx: number, type: string) => {
setOptions(options.map((item) => {
if (item.Type === type) {
// return a copy
return {
...item,
// replace the whole List property with a mapped copy
List: item.List.map( (obj, i) => {
if ( i === idx ) {
// return a copy with a new value of isSelected
return {
...obj,
isSelected: !obj.isSelected,
}
} else {
// return the same item
return obj;
}
}
)
};
} else {
// return the same item
return item;
}
}));
};
The mapping is complicated because you have an object within an array within an object within an array.映射很复杂,因为您在一个数组中的一个数组中有一个 object,在一个数组中的一个 object 中。 You might want to store your selections separate from that messy structure.
您可能希望将您的选择与杂乱的结构分开存储。
We can make the mapping shorter by using the ternary operator instead of if
/ else
.我们可以通过使用三元运算符而不是
if
/ else
来缩短映射。
const handleSelect = async (idx: number, type: string) => {
setOptions(
options.map((item) =>
item.Type === type
? {
...item,
List: item.List.map((obj, i) =>
i === idx
? {
...obj,
isSelected: !obj.isSelected
}
: obj
)
}
: item
)
);
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.