redux 仅在react组件卸载时调度操作

u4dcyp6a  于 9个月前  发布在  React
关注(0)|答案(1)|浏览(128)

我在react-js中使用了多个标签,每个标签内容都是一个组件,比如下面的profile组件,它包含多个输入控件。我想在标签将被更改或组件将被销毁时调度一个updateUser动作。
我使用的是useEffect,所以通过向useEffect传递一个空的依赖数组([]),我可以确保在组件挂载时只运行一次效果,并且在组件卸载时运行清理函数但我想只在卸载组件时分派操作。
请大家帮我实现这个!

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 [phoneExtension, setPhoneExtension] = useState('000');
  const dispatch = useAppDispatch();

  const currentUser = useAppSelector((state) => state.user.currentUser);
  const initialUserState = currentUser || undefined;
  const [user, setUser] = useState<UserDetails | undefined>(initialUserState);
  const handleInputChange = (id: string, value: string) => {
    setUser((prevUser) => ({
      ...(prevUser as UserDetails),
      [id]: value,
    }));
  };

  useEffect(() => {
    let isMounted = true;
    return () => {
      isMounted = false;
      if (currentUser && currentUser.id && user && isMounted) {
        if (!isEqual(currentUser, user)) {
          console.log('Changes detected. Dispatching update.');
          dispatch(updateUser({ id: currentUser.id, user }));
        } else {
          console.log('No changes detected.');
        }
      }
    };
  }, []);

  useEffect(() => {
    return () => {
      if (currentUser && currentUser.id && user) {
        if (!isEqual(currentUser, user)) {
          console.log('Changes detected. Dispatching update.');
          dispatch(updateUser({ id: currentUser.id, user }));
        } else {
          console.log('No changes detected.');
        }
      }
    };
  }, []);

  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={phoneExtension}
                      onChange={(e) => {
                        setPhoneExtension(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='notifySms'>Notify me by dialer</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?.dialer || ''}
                        onChange={(e) => {
                          handleInputChange('dialer', e.target.value);
                        }}
                      />
                    </div>
                  </div>
                  <div className='form-col full-col'>
                    <label htmlFor='notifySms'>Notify me by pager</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?.pager || ''}
                        onChange={(e) => {
                          handleInputChange('pager', e.target.value);
                        }}
                      />
                    </div>
                  </div>
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default Profile;

字符串

4xy9mtcn

4xy9mtcn1#

这些情况的问题是,一方面,你希望清理只运行一次,但另一方面,你不能使用空的依赖数组,因为你需要更新的依赖。解决方案通常是使用ref。

选项1-将currentUseruser保存在ref上:

// create the ref
const userRef = useRef({
  current: currentUser,
  updated: user
});

// always update the ref
useEffect(() => {
  userRef.current = {
    current: currentUser,
    updated: user
  };
});

useEffect(() => () => {
  const { current, updated } = userRef.current;

  if (current?.id && updated) {
    if (!isEqual(current, updated)) {
      console.log('Changes detected. Dispatching update.');
      dispatch(updateUser({
        id: current.id,
        user: updated
      }));
    } else {
      console.log('No changes detected.');
    }
  }
}, [dispatch, updateUser]); // you have to include the functions, but they don't change

字符串

选项2:使用ref来保存卸载状态。这个ref应该由useEffect的cleanup函数更新,该函数在您的调度操作生效之前执行:

// create the ref
const isUnmountingRef = useRef(false);

// update the ref only when unmounting
useEffect(() => () => {
  isUnmountingRef.current = true;
}, []); // empty deps

useEffect(() => () => {
  // check for unmount as well
  if (isUnmountingRef.current && currentUser?.id && user) {
    if (!isEqual(currentUser, user)) {
      console.log('Changes detected. Dispatching update.');
      dispatch(updateUser({ id: currentUser.id, user }));
    } else {
      console.log('No changes detected.');
    }
  }
}); // no depedencies

相关问题