简体   繁体   English

React Native State 在不应该更新的时候更新

[英]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?有谁知道为什么它会更新我的所有变量?

Problem: Mutating State问题:突变 State

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);

Solution: Replace the Object解决方法:更换Object

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM