react-spring的useSpring的onRest回调搭配react-flip-toolkit不触发如何解决
问题核心原因
1. 组件提前卸载
你当前的实现逻辑中,searchResults 是根据 searchQuery 直接过滤得到的数组,当搜索关键词变化时,不符合条件的项会直接从渲染列表中移除,对应的 Card 组件会立刻被React卸载。此时 onExit 回调刚触发,你调用 api.start 启动的退出动画还未执行完成,动画实例就随组件销毁了,自然不会触发 onRest 回调。
2. 依赖版本兼容性问题
你使用的 react-spring@9.1.2 属于v9早期版本,存在多个已知的 onRest 回调不触发的bug,且该版本和 Gatsby@3.4.0 的SSR渲染逻辑存在兼容性问题,也可能导致回调失效。
解决方案
第一步:修复列表渲染逻辑
不要直接过滤掉要删除的项,给数据项加删除标记,等退出动画完成后再真正移除数据:
const Container = () => { const [searchQuery, setSearchQuery] = useState(''); // 原始全量数据 const [allItems, setAllItems] = useState([]); // 带状态的渲染列表,保留待删除项直到动画结束 const [renderItems, setRenderItems] = useState([]); // 搜索时只打删除标记,不直接移除 useEffect(() => { const filtered = allItems.filter(item => item.title.includes(searchQuery) ).map(item => ({...item, isDeleted: false})); // 找出将要删除的项,标记为删除状态 const toDelete = renderItems.filter(item => !filtered.find(f => f.id === item.id) ).map(item => ({...item, isDeleted: true})); setRenderItems([...filtered, ...toDelete]); }, [searchQuery, allItems]); // 真正移除已经完成退出动画的项 const handleRemoveItem = (id) => { setRenderItems(prev => prev.filter(item => item.id !== id)) } return ( <Flipper flipKey={searchQuery} decisionData={searchQuery} className="row" > {renderItems.map((item) => ( <Card key={item.id} item={item} onRemove={handleRemoveItem} /> ))} </Flipper> ); }
第二步:修改Card组件逻辑,动画完成后调用移除方法
const Card = ({ item, onRemove }) => { const [spring, api] = useSpring(() => ({ opacity: 1, transform: 'scale(1)', })); const onExit = (el, index, removeElement) => { api.start({ opacity: 0, transform: 'scale(0.9)', onRest: () => { // 动画完成后真正移除元素 onRemove(item.id) removeElement() }, }); return ( <Flipped flipId={item.vizcartPath} stagger onExit={onExit} > <animated.div className="col-6 col-md-4 col-xl-3 p-5 card-container" style={spring}> TEST </animated.div> </Flipped> ); };
第三步:修复依赖兼容问题
将 react-spring 升级到9.5.0及以上版本,该版本修复了大量v9早期版本的回调bug,和Gatsby 3.x的兼容性更好:
npm install react-spring@latest # 或者用yarn yarn add react-spring@latest
升级后删除项目根目录的 node_modules 文件夹和 package-lock.json/yarn.lock 文件,重新安装依赖即可。
内容的提问来源于stack exchange,提问作者lezan
相关产品推荐
相关产品推荐

