React useState布尔值不更新:自定义Toast二次触发不显示求助
问题根源分析
- 状态不同步:Toast组件内部维护的
showed状态,在用户点击关闭按钮后与父组件传入的isShowedprops脱节——关闭时修改了内部showed为false,但父组件的toast.isShowed仍保持true。 - useEffect未触发:第二次触发错误时,父组件
setToast的isShowed值还是true(未重置),导致Toast组件的isShowedprops没有变化,依赖它的useEffect不会执行,内部showed保持为false,Toast因此不显示。 - 关闭逻辑错误:关闭按钮点击事件中,错误使用props的
isShowed来切换内部状态,而非基于内部showed状态或通知父组件更新状态,导致逻辑混乱。
修复方案
1. 将Toast改为受控组件(推荐)
移除Toast内部的showed状态,直接使用父组件传入的isShowed props,通过回调通知父组件处理关闭操作,让状态统一由父组件管理。
修改后的Toast组件:
export type ToastProps = { color: 'error' | 'success' | 'warning'; message: string; isShowed: boolean; onClose: () => void; // 添加关闭回调 }; const Toast = (props: ToastProps) => { const { color, message, isShowed, onClose } = props; return ( <div className="toast-notification-wrapper" style={{ position: 'fixed', top: '20px', left: '50%', transform: 'translateX(-50%)', zIndex: 9999, padding: '15px', borderRadius: '5px', transition: 'all 0.3s ease-out', opacity: isShowed ? 1 : 0, pointerEvents: isShowed ? 'auto' : 'none', display: 'flex', justifyContent: 'space-between', minWidth: '150px', backgroundColor: color === 'error' ? '#dc3545' : color === 'success' ? '#28a745' : '#ffc107', }} > <p style={{ margin: 0, color: 'white', fontSize: '15px', lineHeight: 1 }}> {message} </p> <span style={{ cursor: 'pointer', display: 'flex', alignContent: 'center', justifyContent: 'center', marginLeft: '30px', }} onClick={onClose} // 调用父组件的关闭回调 > <FontAwesomeIcon icon={faClose} style={{ color: 'white', fontSize: '15px' }} /> </span> </div> ); };
2. 更新Login组件的状态管理
在Login组件中实现Toast的关闭回调,确保每次触发错误时正确设置Toast状态:
修改后的Login组件相关部分:
const Login = (props: { isCompleted: boolean; isLoggedIn: boolean }) => { // ... 其他原有状态 // 处理Toast关闭 const handleToastClose = () => { setToast(prev => ({ ...prev, isShowed: false })); }; const handleSubmit = async (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); try { await login({ username, password }); window.location.href = '/'; } catch (error: any) { // 直接设置isShowed为true,确保Toast触发显示 setToast({ isShowed: true, color: 'error', message: error.response.data?.message || 'Unexpected Error. Please try again later', }); } }; return ( <div className="login-form"> <Toast isShowed={toast.isShowed} color={toast.color} message={toast.message} onClose={handleToastClose} // 传递关闭回调 ></Toast> {/* ... 其他原有表单内容 */} </div> ); };
3. 可选:添加自动关闭功能(优化体验)
如果需要Toast自动关闭,可在Toast组件中添加定时器:
const Toast = (props: ToastProps) => { const { color, message, isShowed, onClose } = props; useEffect(() => { let timer: NodeJS.Timeout; if (isShowed) { // 3秒后自动关闭 timer = setTimeout(() => { onClose(); }, 3000); } return () => clearTimeout(timer); }, [isShowed, onClose]); // ... 其余渲染代码 };
修复逻辑说明
- 受控组件模式让状态统一由父组件管理,彻底避免子组件内部状态与props的不一致问题。
- 关闭回调确保父组件同步更新状态,下次触发错误时
isShowed从false变为true,Toast能正常触发显示。 - 移除子组件内部的
showed状态,减少状态维护复杂度,避免不必要的同步逻辑。
内容的提问来源于stack exchange,提问作者EQ0920
相关产品推荐
相关产品推荐

