You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Redux Toolkit中Dispatch动作过慢致UI冻结问题排查与求助

勾选清单状态更新导致UI冻结的问题排查与优化建议

核心问题

开发类似Trello的嵌套勾选清单时,创建清单和添加项功能正常,但切换项的完成状态(勾选/取消)时,dispatch更新全局状态的操作过慢,导致UI冻结。曾考虑用异步dispatch解决,不确定是否可行。

相关状态更新代码

Dispatch调用

dispatch(
  updatecarditem({
    checklistId,
    itemId: index,
    itemStatus: checked,
  }),
);

Reducer处理逻辑

updatecarditem: (state: any, {payload}) => {
  const checklistId = payload.checklistId;
  const itemId = payload.itemId;

  let item =
    state.entities[0]?.checklist?.[checklistId]?.checklistItem?.[itemId];
  if (payload.itemTitle != undefined) item.itemTitle = payload.itemTitle;
  if (payload.itemStatus != undefined) item.itemStatus = payload.itemStatus;
},

组件结构补充

主清单组件(CheckListItem)

export const CheckListItem: FC<CheckListItemProps> = ({item, index}) => {
  const card = useSelector<any, any>(cardDetails);

  const [value, setValue] = useState(item?.checklistTitle);
  const [newItem, setNewItem] = useState('');

  const dispatch = useDispatch();

  useEffect(() => {
    setValue(item?.checklistTitle);
  }, [item?.checklistTitle]);

  const toggleVisible = () => {
    setVisible((prev) => !prev);
  };

  const addCheckistItem = useCallback(() => {
    if (!newItem.length) {
      return;
    }
    const newCItem = {itemTitle: newItem, itemStatus: false};

    updateChecklist([...item?.checklistItem, newCItem]);
    setNewItem('');
  }, [newItem, item?.checklistItem]);

  const updateChecklist = useCallback(
    (clitem = item?.checklistItem) => {
      if (value.length < 3) {
        showToast(
          'error',
          'Checklist name should contain atleast 3 characters',
        );
        return;
      }
      let newls = [...card?.checklist];
      newls[index].checklistTitle = value;
      newls[index].checklistItem = clitem;

      dispatch(
        updatecard({
          checklist: newls,
        }),
      );
    },
    [value, card?.checklist],
  );

  return (
      <VStack style={styles.cardinfoviews}>
       
          <CustomInput
            onSubmit={updateChecklist}
            value={value}
            setValue={setValue}
          />
      
          <VStack style={styles.cardinfoviews}>
            {item?.checklistItem?.map((citem: any, itemindex: number) => (
              <CheckItem // THIS IS THE NESTED COMPONENT
                key={itemindex}
                citem={citem}
                index={itemindex}
                checklistId={index}
              />
            ))}

            <Input
              onSubmitEditing={addCheckistItem}
              value={newItem}
              onChangeText={setNewItem}
              maxLength={40}
              variant="underlined"
              placeholder="Add item..."
            />
          </VStack>
        )
      </VStack>
  );
};

嵌套项组件(CheckItem)

const CheckItem: FC<CheckItemProps> = ({citem, index, checklistId}) => {
  const [value, setValue] = useState(citem?.itemTitle);
  const [checked, setChecked] = useState(citem?.itemStatus);

  const dispatch = useDispatch();

  useEffect(() => {
    setValue(citem?.itemTitle);
  }, [citem?.itemTitle]);

  useEffect(() => {
    setChecked(citem?.itemStatus);
  }, [citem?.itemStatus]);

  const updatecheckItem = useCallback(
    (checked = citem?.itemStatus) => {
      setChecked(checked);
      dispatch(
        updatecarditem({
          checklistId,
          itemId: index,
          itemStatus: checked,
        }),
      );
    },
    [checklistId, index],
  );

  const updateItemName = useCallback(() => {
    if (value.length < 3) {
      showToast('error', 'Item name should contain atleast 3 characters');
      return;
    }

    dispatch(
      updatecarditem({
        checklistId,
        itemId: index,
        itemTitle: value,
      }),
    );
  }, [value, index, checklistId]);


  return (
      <Checkbox
        value={citem?.itemTitle}
        isChecked={checked}
        onChange={updatecheckItem}>
          <Input
            w="85%"
            onSubmitEditing={updateItemName}
            variant="unstyled"
            value={value}
            onChangeText={setValue}
            multiline
            numberOfLines={1}
            blurOnSubmit
          />
      </Checkbox>
  );
};

已定位问题及待优化点

已确认问题源于过多不必要的重渲染,以下是代码中触发重渲染的关键点:

  • 主组件CheckListItem:
    • useSelector直接返回整个card对象,card内任何微小变化都会触发组件重渲染,建议改用精确选择器,仅提取当前清单所需数据。
    • updateChecklist回调依赖card?.checklist,每次checklist变化都会生成新的回调,导致子组件CheckItem频繁接收新props触发重渲染。
    • 遍历checklistItem时用itemindex作为key,列表项增删或顺序变化时,React会错误复用组件,引发不必要重渲染,建议改用每个item的唯一ID作为key。
  • 嵌套组件CheckItem:
    • 同时维护本地状态value、checked和全局状态,通过useEffect同步会导致额外重渲染,建议直接使用全局状态作为Checkbox和Input的数据源,去掉冗余的本地状态同步。
    • updatecheckItem回调依赖项未包含citem?.itemStatus,可能捕获旧值;且父组件重渲染时回调引用变化,会触发Checkbox重新渲染。
  • Reducer逻辑:
    • 直接修改state中的嵌套对象(item.itemStatus = payload.itemStatus),违反Redux不可变性原则,导致React无法正确检测状态变化,引发异常重渲染或更新不及时。需创建嵌套对象副本,修改后替换原对象。

关于异步dispatch:Redux的dispatch本身是同步的,可通过Thunk或Saga中间件实现异步action,但当前问题核心是重渲染而非异步操作,优先解决重渲染才是关键,异步dispatch无法直接解决UI冻结。


内容的提问来源于stack exchange,提问作者Irfan wani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 19:05:19