首页 > 解决方案 > 在 React 中处理两个相似类型的 useEffect 函数的最佳方法是什么?

问题描述

所以我在 React 中有一个表单,我有一个用户名字段和电子邮件字段。在用户停止输入 800 毫秒后,我正在检查用户名/电子邮件(基于用户正在输入的字段)是否可用(不被其他人使用)。

我最终有两个非常相似的 useEffect 函数对用户名和电子邮件执行相同的操作,只是它们发送请求的 URL 和它们正在监视的变量不同。

我在这里分享代码片段。我在这里使用 useReducer。

初始状态:

const initialState = {
      username: {
      value: '',
      hasErrors: false,
      errorMessage: '',
      isUnique: false,
      checkCount: 0,
    },
    email: {
      value: '',
      hasErrors: false,
      errorMessage: '',
      isUnique: false,
      checkCount: 0,
    },

    submitCount: 0,
}

减速机功能:

//using immer
function myReducer(draft, action) {
    switch (action.type) {      
      case 'usernameAfterDelay':       
          draft.username.checkCount++;        
        return;
      case 'usernameUniqueResults':
        if (action.value) {
          draft.username.hasErrors = true;
          draft.username.isUnique = false;
          draft.username.message = 'That username is already taken.';
        } else {
          draft.username.isUnique = true;
        }
        return;     
      case 'emailAfterDelay':      
          draft.email.checkCount++;        
        return;
      case 'emailUniqueResults':
        if (action.value) {
          draft.email.hasErrors = true;
          draft.email.isUnique = false;
          draft.email.message = 'That email is already being used.';
        } else {
          draft.email.isUnique = true;
        }
        return;   
      default:
        return;
    }
  }

   const [state, dispatch] = useImmerReducer(ourReducer, initialState);

我的 useEffect 功能非常相似

useEffect 用于应用去抖动到用户输入


  useEffect(() => {
    if (state.username.value) {
      const delay = setTimeout(() => {
        dispatch({ type: 'usernameAfterDelay' });
      }, 800);
      return () => clearTimeout(delay);
    }    
  }, [state.username.value]);

  
  useEffect(() => {
    if (state.email.value) {
      const delay = setTimeout(() => {
        dispatch({ type: 'emailAfterDelay' });
      }, 800);
      return () => clearTimeout(delay);
    }    
  }, [state.email.value]);

useEffect 用于实际进行 api 调用

  useEffect(() => {
    if (state.username.checkCount) {
      const ourRequest = Axios.CancelToken.source();
      async function fetchResults() {
        try {
          const response = await Axios.post(
            '/doesUsernameExist',
            { username: state.username.value },
            { cancelToken: ourRequest.token }
          );
          dispatch({ type: 'usernameUniqueResults', value: response.data });
        } catch (e) {
          console.log('There was a problem or the request was cancelled.');
        }
      }
      fetchResults();
      return () => ourRequest.cancel();
    }  
  }, [state.username.checkCount]);

  useEffect(() => {
    if (state.email.checkCount) {
      const ourRequest = Axios.CancelToken.source();
      async function fetchResults() {
        try {
          const response = await Axios.post(
            '/doesEmailExist',
            { email: state.email.value },
            { cancelToken: ourRequest.token }
          );
          dispatch({ type: 'emailUniqueResults', value: response.data });
        } catch (e) {
          console.log('There was a problem or the request was cancelled.');
        }
      }
      fetchResults();
      return () => ourRequest.cancel();
    } 
  }, [state.email.checkCount]);

JSX如下

<div>
<label htmlFor="username-register" className="text-muted mb-1">
    <small>Username</small>
</label>  
<input
  id="username-register"
  onChange={(e) =>
    dispatch({
      type: 'usernameImmediately',
      value: e.target.value,
    })
  }
  value={state.username.value}
/>
<CSSTransition
  in={state.username.hasErrors}
  timeout={330}
  classNames="liveValidateMessage"
  unmountOnExit
>
  <div className="alert alert-danger small liveValidateMessage">
    {state.username.message}
  </div>
</CSSTransition>
<div>
<div>
<label htmlFor="email-register" className="text-muted mb-1">
    <small>Email</small>
    </label>
    <input
    id="email-register" 
    onChange={(e) =>
    dispatch({
      type: 'emailImmediately',
      value: e.target.value,
    })
    }
    />
    <CSSTransition
    in={state.email.hasErrors}
    timeout={330}
    classNames="liveValidateMessage"
    unmountOnExit
    >
    <div className="alert alert-danger small liveValidateMessage">
    {state.email.message}
    </div>
    </CSSTransition>
</div>

正如你所看到的,这里有很多重复的代码,我想知道有没有更好的方法来处理事情。

标签: javascriptreactjsreact-reduxreact-hooksuse-effect

解决方案


为什么在你的 onchanged 事件中更好地使用 useEffect 中的所有这些?

let timer = [];  //this is a global variable, need to be outside your component so the value does not get reset on every render.

const MyComponent = () => {

      const [ isUsernameOk, setIsUsernameOk ] = useState(false)

      ......

      const getResponse = (url, body) => await Axios.post(
         url, body,
        { cancelToken: ourRequest.token }
      ); //u can put everything in one line.



    const onChange = () => {

       const { name, value }  = e.target
       dispatch({ type: 'usernameImmediately', value: e.target.value })
       clearTimeout(timer[name]);
       timer[name] = setTimeout(dispatch({ type: 'usernameAfterDelay' }),899)

       // OR

       timer[name] == setTimeout(async () => {
              const response = await getResponse('/emailCheck', { email: test@email.com })
             //do whatever u want with response
             //do your dispatch or,

             setIsUsernameOk(true)
       })

}


return (
...............
{ isUserNameOk && <span> Username is available </span>
)

}

无论您在做什么,都不需要使用 useEffect。不必要地使用 useEffect 是无效的,并且可能不必要地导致循环错误(以及不必要的重新渲染)

这是单查?你真的需要通过所有的 redux 代码来使你的组件复杂化吗?地方状态不足吗?


推荐阅读