简体   繁体   English

使用 setState() 更新数组时发生反应 / TypeScript 错误

[英]React / TypeScript error when updating array using setState()

I am looking to modify a property of one of the items in the array using the updater returned from setState.我正在寻找使用从 setState 返回的更新程序来修改数组中一项的属性。 The function is passed as props to the child, who then call this with their own index to change their status. function 作为道具传递给孩子,然后孩子用自己的索引调用它来改变他们的状态。

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

When running with setHabitList(list);使用setHabitList(list); the array gets cleared and becomes undefined, so I am unable to use it after I call this function once.数组被清除并变得未定义,所以在我调用这个 function 一次后我无法使用它。

The error that keeps appearing is:不断出现的错误是:

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[]>'

I am treating the state as immutable and attempting (I think) to set the state.我将 state 视为不可变的,并尝试(我认为)设置 state。 When debugging, everytime I click I empty the array for my habitsList.调试时,每次单击我都会清空我的习惯列表的数组。 Further up, I define the state as:更进一步,我将 state 定义为:

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

If I try to setState in other ways, the array becomes undefined or blank and loses information.如果我尝试以其他方式设置状态,则数组将变为未定义或空白并丢失信息。 I should clarify, this is a side project written w/ react-native.我应该澄清一下,这是一个使用 react-native 编写的附带项目。

MRE Edit: MRE编辑:

2 components in a react-native app, discarding css & irrelevant imports: Chain.tsx 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;

Habit.tsx习惯.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;

When I press a habit, it calls tryMarkHabitAsComplete successfully, however it appears to clear the array - the array afterwards becomes undefined as far as I can tell.当我按下一个习惯时,它会成功调用 tryMarkHabitAsComplete,但是它似乎清除了数组 - 据我所知,数组随后变得未定义。

Issue问题

If I had to guess I'd say you've a stale enclosure of your habitList state in the callback, which itself is a stale enclosure in your state.如果我不得不猜测,我会说您在回调中有一个陈旧的habitList state 外壳,它本身就是您的 state 中的一个陈旧外壳。

Solution解决方案

I suggest to instead use a functional state update in your handler.我建议在您的处理程序中使用功能性 state 更新。 A functional update allows your state to be updated from the previous state, not the state from the render cycle the update was enqueued in. It's a small, subtle, but important distinction.功能更新允许您的 state 从之前的 state 进行更新,而不是从渲染周期中的 state 更新被排入队列。这是一个很小但很重要的区别。

I also suggest to not enclose the handler in an enclosure in state either.我还建议不要将处理程序封装在 state 的外壳中。 Just pass it to Habit as a normal callback, this way you avoid any stale enclosures.只需将其作为正常回调传递给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>
  );
};

Edit for isComplete Style编辑isComplete样式

Habit should just consume the passed isComplete prop to set the desired CSS style. 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>
  );
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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