【问题标题】:React Native State Updated when it should notReact Native State 在不应该更新的时候更新
【发布时间】:2021-05-20 00:36:36
【问题描述】:

我有来自控制器的filterData,所以我将filterData 设置为选项,用于UI, 像这样

const [state, setState] = React.useState({
    options: filterData,
  });

过滤数据是这样的

[{"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"}]

我将它呈现在flatlist 然后map 列表中,这是我的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>
      );
     }}
   />

正如你从上面看到的,我会有一些buttons。如果点击button,它将调用handleSelect函数。这是这个

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

我没有像下面的代码那样改变选项的状态,只有console.log

setState((prevState) => ({
      ...prevState,
      options: arr,
    }));

问题是,当我单击并运行该函数时,在日志中,state.options 也会发生变化。 它将具有与arr 相同的数据。即使filterData 也会在我console.loghandleselect 中发生变化。我已经检查了我的控制器,它没有触发任何功能。 有谁知道为什么它会更新我的所有变量?

【问题讨论】:

    标签: javascript typescript react-native mapping use-state


    【解决方案1】:

    问题:变异状态

    当您创建像let arr = [...state.options]; 这样的复制数组时,变量arr 是一个新数组,但该数组的元素引用的对象与原始state.options 中的对象相同。因此,当您在 arr 的元素上切换 isSelected 时,您也在更改状态中的值。

    这一行是发生突变的地方:

    item.List[idx].isSelected = !item.List[idx].isSelected;
    

    您的arr.map() 实际上并没有返回任何内容。 .map() 旨在将元素映射到新值,因此您的回调需要返回新值。您可能打算使用.forEach()。虽然我们确实想要.map(),但我们需要返回item

    作为旁注,每个组件可以有多个useState 挂钩,因此您不需要像在类组件中那样使用对象属性。你所拥有的不是问题,但你可以让它变得更简单:

    const [options, setOptions] = React.useState(filterData);
    

    解决方法:替换对象

    您需要为要更改属性的任何对象创建一个复制对象。您实际上不需要复制整个数组,因为.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;
        }
      }));
    };
    

    映射很复杂,因为数组中的对象中包含数组中的对象。您可能希望将您的选择与杂乱的结构分开存储。

    我们可以通过使用三元运算符而不是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
          )
        );
      };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-30
      • 2010-09-22
      • 1970-01-01
      • 1970-01-01
      • 2018-12-16
      • 2020-07-22
      相关资源
      最近更新 更多