React Native聊天线程侧边栏模态框提交重命名后异常重启并冻结的问题
兄弟我太懂你这种卡死的糟心感了——明明删除功能好好的,一加输入框就出妖蛾子,还得重启才能恢复,简直头大😂。结合你说的“去掉TextInput就正常”这个关键线索,大概率是TextInput触发的频繁重渲染和Redux更新、Modal状态的时机冲突搞出来的问题,给你捋几个排查和解决的思路:
1. 先调整重命名逻辑的执行顺序,避免Modal“死灰复燃”
你现在是先dispatch更新Redux,再关闭Modal,但React的setState是异步的,Redux更新触发组件重渲染时,pending可能还没来得及被置为null,导致Modal又被重新渲染出来,和之前的实例叠加就会冻结。咱换个顺序试试:
const handleRename = async () => { if (!pending) return; if (title.length < 3) { setTitleErr('Title must be longer than 3 characters'); return; } setTitleErr(''); const {threadUuid} = pending; setRenaming(true); Keyboard.dismiss(); try { const newTitle = await chatService.renameChatThread(threadUuid, title); console.log('newTitle', newTitle.title); // 先关闭Modal,把pending置为null,再更新Redux closeModal(); dispatch(renameThread(threadUuid, newTitle.title)); } catch (e) { console.warn('[renameChatThread]', e); setTitleErr('Failed to rename thread'); setRenaming(false); // 失败后保持Modal打开,让用户重试 } finally { // 成功关闭Modal后再重置renaming状态 if (!titleErr) { setRenaming(false); } } };
2. 排查动画干扰,先去掉TextInput的Animated包裹
你给TextInput套了Animated.View exiting={ZoomOut},这个退出动画可能会延迟Modal的卸载过程。当Redux更新触发重渲染时,旧的Modal还没完全消失,新的Modal又因为pending没及时清空而渲染,就会导致视图叠加冻结。先把动画去掉测试看看:
// 把原来的Animated包裹去掉,直接渲染TextInput {renamingUI ? ( <TextInput style={[ styles.input, { borderColor: titleErr ? '#E34234' : '#CCC' }, ]} placeholder="New title" placeholderTextColor="#888" value={title} onChangeText={setTitle} /> ) : null}
3. 优化重渲染,把TextInput拆成独立组件减少渲染压力
TextInput每输入一个字符都会触发setTitle,导致整个SidebarContent组件重渲染,FlatList的所有线程行也会跟着重新渲染,这会放大状态更新的冲突问题。咱把输入相关逻辑拆成独立子组件,用React.memo包裹减少不必要的重渲染:
// 单独写一个重命名输入组件 const RenameInput = React.memo(({ title, setTitle, titleErr, colors }) => ( <> <TextInput style={[ styles.input, { borderColor: titleErr ? '#E34234' : '#CCC' }, ]} placeholder="New title" placeholderTextColor="#888" value={title} onChangeText={setTitle} /> {titleErr && ( <Text style={{ color: '#E34234', marginBottom: 8 }}> {titleErr} </Text> )} </> )); // 然后在Modal里用这个组件 {renamingUI ? ( <RenameInput title={title} setTitle={setTitle} titleErr={titleErr} colors={colors} /> ) : null}
4. 加个保险:用useEffect监听Redux线程变化,强制关闭Modal
万一前面的调整还是有漏网之鱼,咱加个兜底逻辑——当Redux里的线程标题更新完成后,自动关闭Modal:
useEffect(() => { if (pending) { const updatedThread = chatThreads.find(t => t.threadUuid === pending.threadUuid); // 目标线程的标题已经变成新输入的title,就强制关闭Modal if (updatedThread && updatedThread.title === title) { closeModal(); } } }, [chatThreads, pending, title]);
这类问题本质就是状态更新时机和组件重渲染的冲突,TextInput的频繁状态更新相当于“火上浇油”,调整执行顺序、减少不必要的重渲染、排查动画干扰基本就能解决啦。
内容来源于stack exchange

