React组件卸载时仅Dispatch Action的实现方案求助
解决方案:组件卸载时触发updateUser Action
问题分析
你当前的代码存在几个关键问题:
- 第一个
useEffect里的isMounted逻辑完全错误——清理函数里先把isMounted设为false,紧接着判断isMounted,导致后续的dispatch代码永远不会执行。 - 第二个
useEffect依赖数组为空,会触发闭包陷阱:清理函数捕获的currentUser和user是组件挂载时的初始值,不是卸载时的最新值,没法正确判断内容是否修改。 phoneExtension是单独维护的状态,没有同步到user对象里,卸载时该字段的修改会丢失,不会被dispatch到store。
修正后的代码
import React, { useEffect, useState } from 'react'; import '../User.scss'; import iconPhone from '../../../images/icon-phone.png'; import iconMail1 from '../../../images/icon-mail-01.png'; import iconMessageTextSquare1 from '../../../images/icon-message-text-square-01.png'; import { useAppDispatch, useAppSelector } from '../../../hooks/storeHooks'; import { UserDetails } from '../model/userDetails'; import { updateUser } from '../UserSlice'; import { isEqual } from 'lodash'; const Profile: React.FC = () => { const dispatch = useAppDispatch(); const currentUser = useAppSelector((state) => state.user.currentUser); // 初始化时合并phoneExtension到user状态 const initialUserState = currentUser ? { ...currentUser, phoneExtension: currentUser.phoneExtension || '000' } : undefined; const [user, setUser] = useState<UserDetails & { phoneExtension?: string } | undefined>(initialUserState); const handleInputChange = (id: string, value: string) => { setUser((prevUser) => ({ ...(prevUser as UserDetails & { phoneExtension?: string }), [id]: value, })); }; // 正确的useEffect:依赖包含currentUser和user,确保清理函数捕获最新值 useEffect(() => { // 清理函数在组件卸载时执行 return () => { if (currentUser?.id && user) { // 对比当前store值和本地修改后的值 const updatedUser = { ...user }; // 移除phoneExtension单独字段(如果UserDetails不包含的话,根据实际模型调整) delete updatedUser.phoneExtension; if (!isEqual(currentUser, updatedUser)) { console.log('Changes detected. Dispatching update.'); dispatch(updateUser({ id: currentUser.id, user: updatedUser })); } else { console.log('No changes detected.'); } } }; }, [currentUser, user, dispatch]); // 必须添加所有用到的依赖 return ( <div className='flex flex-row flex-grow profile-block'> <div className='card profile-card'> <div className='header'> <span className='title'>Profile</span> <div>update your personal and company details here.</div> </div> <div className='body'> <div className='profile-info'> <div className='protext'> <div className='text'>Personal info</div> <p>Personal details and profile picture</p> </div> <div className='card'> <form className='register-form'> <div className='form-row'> <div className='form-col'> <label htmlFor='firstName'>First Name:</label> <input type='text' id='firstName' className='custom-input' value={user?.firstName || ''} onChange={(e) => handleInputChange('firstName', e.target.value)} readOnly={false} /> </div> <div className='form-col'> <label htmlFor='lastName'>Last Name:</label> <input type='text' id='lastName' className='custom-input' value={user?.lastName || ''} onChange={(e) => handleInputChange('lastName', e.target.value)} /> </div> </div> <div className='form-row'> <div className='form-col full-col'> <label htmlFor='jobtitle'>Job Title:</label> <input type='text' id='jobtitle' className='custom-input' value={user?.jobTitle || ''} onChange={(e) => handleInputChange('jobTitle', e.target.value)} /> </div> </div> <div className='form-row'> <div className='form-col col60'> <label htmlFor='phoneNumber'>Office Number</label> <div className='input-group'> <div className='icon-block'> <img src={iconPhone} alt='icon phone' /> </div> <input type='text' id='phoneNumber' className='custom-input' value={user?.phoneNumber || ''} onChange={(e) => handleInputChange('phoneNumber', e.target.value)} /> </div> </div> <div className='form-col col40'> <label htmlFor='phoneExtension'>Phone Extension:</label> <input type='text' id='phoneExtension' className='custom-input' value={user?.phoneExtension || '000'} onChange={(e) => handleInputChange('phoneExtension', e.target.value)} /> </div> </div> </form> </div> </div> <br /> <div className='divider'></div> <br /> <br /> <div className='profile-info'> <div className='protext'> <div className='text'>Notification Contact Information</div> <p>Details here will be blah blah blah for contact information for notifications. </p> </div> <div className='card'> <form className='register-form'> <div className='form-row'> <div className='form-col full-col'> <label htmlFor='notifyEmail'>Notify me by email</label> <div className='input-group'> <div className='icon-block'> <img src={iconMail1} alt='icon email' /> </div> <input type='text' id='notifyEmail' className='custom-input' value={user?.email || ''} onChange={(e) => handleInputChange('email', e.target.value)} /> </div> </div> <div className='form-col full-col'> <label htmlFor='notifySms'>Notify me by sms</label> <div className='input-group'> <div className='icon-block'> <img src={iconMessageTextSquare1} alt='icon sms' /> </div> <input type='text' id='notifySms' className='custom-input' value={user?.phoneNumber || ''} onChange={(e) => handleInputChange('phoneNumber', e.target.value)} /> </div> </div> <div className='form-col full-col'> <label htmlFor='notifyDialer'>Notify me by dialer</label> <div className='input-group'> <div className='icon-block'> <img src={iconMessageTextSquare1} alt='icon dialer' /> </div> <input type='text' id='notifyDialer' className='custom-input' value={user?.dialer || ''} onChange={(e) => handleInputChange('dialer', e.target.value)} /> </div> </div> <div className='form-col full-col'> <label htmlFor='notifyPager'>Notify me by pager</label> <div className='input-group'> <div className='icon-block'> <img src={iconMessageTextSquare1} alt='icon pager' /> </div> <input type='text' id='notifyPager' className='custom-input' value={user?.pager || ''} onChange={(e) => handleInputChange('pager', e.target.value)} /> </div> </div> </div> </form> </div> </div> </div> </div> </div> ); }; export default Profile;
关键修正点
- 合并状态:把
phoneExtension合并到user状态中,避免单独维护导致的修改丢失问题。 - 修复闭包问题:
useEffect的依赖数组添加currentUser、user和dispatch,确保清理函数能捕获到最新的状态值。 - 移除无效的isMounted判断:清理函数本身就是在组件卸载时执行,不需要额外判断挂载状态。
- 修正输入ID:把重复的
notifySmsID改为对应的notifyDialer、notifyPager,避免DOM元素ID冲突。
内容的提问来源于stack exchange,提问作者radhey_mishra
相关产品推荐
相关产品推荐

