首页 > 解决方案 > 使用 setState() 更新数组时出现 React / TypeScript 错误

问题描述

我正在寻找使用从 setState 返回的更新程序来修改数组中一项的属性。该函数作为道具传递给孩子,然后孩子用自己的索引调用它来改变他们的状态。

  const tryMarkHabitAsComplete = (index: number) => {
    let list: HabitType[] = [...habitList];
    let habit = {
      ...list[index],
      isComplete: true,
    };
    list[index] = habit;
    setHabitList({list});
  };

使用 setHabitList(list);数组运行时会被清除并变为未定义,因此在调用此函数一次后我无法使用它。

不断出现的错误是:

Argument of type '{ list: HabitType[]; }' is not assignable to parameter of type 'SetStateAction<HabitType[]>'. Object literal may only specify known properties, and 'list' does not exist in type 'SetStateAction<HabitType[]>'

我将状态视为不可变的,并尝试(我认为)设置状态。调试时,每次单击我都会清空我的习惯列表的数组。更进一步,我将状态定义为:

const [habitList, setHabitList] = useState<HabitType[]>([]);

如果我尝试以其他方式设置状态,则数组将变为未定义或空白并丢失信息。我应该澄清一下,这是一个使用 react-native 编写的副项目。

MRE编辑:

react-native 应用程序中的 2 个组件,丢弃 css 和不相关的导入: Chain.tsx

export type HabitType = {
  text: string;
  index: number;
  isComplete: boolean;
  tryMarkHabit: (index: number) => void;
};

const Chain = (props: any) => {
  const [habitList, setHabitList] = useState<HabitType[]>([]);

  // Set the initial state once w/ dummy values
  useEffect(() => {
    setHabitList([
      {
        text: "1",
        index: 0,
        isComplete: false,
        tryMarkHabit: tryMarkHabitAsComplete,
      },
      {
        text: "2",
        index: 1,
        isComplete: false,
        tryMarkHabit: tryMarkHabitAsComplete,
      }
    ]);
  }, []);

  // Only is previous item is complete, let habit be marked as complete
  const tryMarkHabitAsComplete = (index: number) => {
    let list: HabitType[] = [...habitList];
    let habit = {
      ...list[index],
      isComplete: true,
    };
    list[index] = habit;
    setHabitList(list);
  };

  let habitKeyCount = 0;

  return (
    <View style={styles.container}>
      {habitList.map((habit) => (
        <Habit key={habitKeyCount++} {...habit} />
      ))}
    </View>
  );
};

export default Chain;

习惯.tsx

import { HabitType } from "./Chain";

const Habit = ({ text, index, isComplete, tryMarkHabit }: HabitType) => {
  const [complete, setComplete] = useState<boolean>(false);

  return (
    <TouchableOpacity
      style={complete ? styles.completeHabit : styles.uncompleteHabit}
      onPress={() => tryMarkHabit(index)}
    >
      <Text style={styles.chainText}>{text}</Text>
    </TouchableOpacity>
  );
};

export default Habit;

当我按下一个习惯时,它会成功调用 tryMarkHabitAsComplete,但是它似乎清除了数组 - 据我所知,数组随后变得未定义。

标签: javascriptreactjstypescriptreact-nativereact-hooks

解决方案


问题

如果我不得不猜测,我会说你habitList在回调中有一个陈旧的外壳,它本身就是你状态的一个陈旧的外壳。

解决方案

我建议改为在您的处理程序中使用功能状态更新。功能更新允许从之前的状态更新您的状态,而不是更新排队的渲染周期中的状态。这是一个小而微妙但重要的区别。

我还建议不要将处理程序封闭在状态的外壳中。只需将其Habit作为普通回调传递给,这样您就可以避免任何陈旧的外壳。

const Chain = (props: any) => {
  const [habitList, setHabitList] = useState<HabitType[]>([]);

  // Set the initial state once w/ dummy values
  useEffect(() => {
    setHabitList([
      {
        text: "1",
        index: 0,
        isComplete: false,
      },
      {
        text: "2",
        index: 1,
        isComplete: false,
      }
    ]);
  }, []);

  // Only is previous item is complete, let habit be marked as complete
  const tryMarkHabitAsComplete = (index: number) => {
    setHabitList(list => list.map((habit, i) => i === index ? {
      ...habit,
      isComplete: true,
    } : habit);
  };

  let habitKeyCount = 0;

  return (
    <View style={styles.container}>
      {habitList.map((habit) => (
        <Habit
          key={habitKeyCount++}
          {...habit}
          tryMarkHabit={tryMarkHabitAsComplete}
        />
      ))}
    </View>
  );
};

编辑isComplete样式

Habit应该只使用传递的isComplete道具来设置所需的 CSS 样式。

const Habit = ({ text, index, isComplete, tryMarkHabit }: HabitType) => {
  return (
    <TouchableOpacity
      style={isComplete ? styles.completeHabit : styles.uncompleteHabit}
      onPress={() => tryMarkHabit(index)}
    >
      <Text style={styles.chainText}>{text}</Text>
    </TouchableOpacity>
  );
};

推荐阅读