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

react-spring useTransition列表全量动画异常求助

解决react-spring useTransition列表动画异常问题

核心问题拆解

  • 全量动画触发根源:要么列表项的key不稳定(比如用数组索引),要么组件未正确缓存,导致删除顶部项时所有子组件被迫重新渲染,触发不必要的过渡动画
  • 上下删除表现差异:从下往上删时,剩余项的key和对应内容的映射关系没被打乱;从上往下删时,如果用索引当key,后面的项会因为索引前移被react-spring判定为“旧项移除、新项创建”,进而触发全量动画

关键修复步骤

1. 给列表项用唯一稳定的key

绝对别用数组索引当key,必须用每个Item自身的唯一标识(比如item.id)。react-spring的useTransition完全依赖key识别元素的增删,key不稳定直接导致过渡逻辑混乱。

2. 用React.memo缓存单个Item组件

把Item组件用React.memo包裹,避免父组件重渲染时无意义的子组件更新:

const Item = React.memo(({ item, style, onDelete }) => {
  return (
    <animated.div style={style}>
      <div>{item.content}</div>
      <button onClick={() => onDelete(item.id)}>删除</button>
    </animated.div>
  );
});

注意:如果Item接收的props是引用类型(比如删除回调),必须保证props的引用稳定,否则memo会直接失效。

3. 结合RTK Query稳定数据与回调

  • 从RTK Query取数据时,直接用useGetItemsQuery返回的data即可,RTK Query会自动缓存数据,避免不必要的重渲染
  • 删除操作的回调用useCallback包裹,确保引用稳定,防止触发Item组件的无意义更新:
const ItemList = () => {
  const { data: items = [] } = useGetItemsQuery();
  const dispatch = useDispatch();

  const handleDelete = useCallback((itemId) => {
    dispatch(deleteItem(itemId));
  }, [dispatch]);

  const transitions = useTransition(items, {
    keys: item => item.id,
    from: { opacity: 0, height: 0 },
    enter: { opacity: 1, height: 'auto' },
    leave: { opacity: 0, height: 0 },
  });

  return (
    <div>
      {transitions((style, item) => (
        <Item key={item.id} item={item} style={style} onDelete={handleDelete} />
      ))}
    </div>
  );
};

4. 检查Redux状态更新逻辑

确保删除action的reducer是不可变更新,绝对不能直接修改原数组,必须返回新数组:

// RTK Slice示例
const itemsSlice = createSlice({
  name: 'items',
  initialState: [],
  reducers: {
    deleteItem: (state, action) => {
      // 正确写法:过滤出不等于目标id的项,返回新数组
      return state.filter(item => item.id !== action.payload);
      // 错误写法:直接修改原数组,比如state.splice(...)
    },
  },
});

如果用RTK Query的mutation,也要保证mutation返回的更新逻辑是不可变的。

上下删除表现差异的本质

如果之前用索引当key,从上往下删时,比如删除索引0的项,原来索引1的项会变成索引0,react-spring会认为原来的索引0项被移除,索引1的项是“新创建”的,所以会触发所有后续项的离开+进入动画;而从下往上删时,前面的项索引不变,react-spring只会处理被删除的项,所以表现正常。换成唯一id当key后,每个项的身份唯一,不管删除哪个位置的项,react-spring都能精准识别出被删除的项,只触发它的离开动画,其他项不受影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 18:45:49