如何在useEffect中设置状态完成后调用函数?

时间:2021-02-12 09:06:39

标签: javascript reactjs asynchronous react-hooks use-effect

我想仅在 customFunction 完成设置 customEffect 状态时运行 isReady。并且 customFunction 应该只运行一次,无论 isReady 是设置为 false 还是 true,只要它在设置后运行即可。

import customFunction from 'myFile';

export const smallComponent = () => {
  const [isReady, setIsReady] = useState(false);

  useEffect(() => {
    const customEffect = async () => {
      try {
        const response = await get(
          `some-api.com`,
        );
        return setIsReady(response); // response can be true or false
      } catch {
        return null;
      }
    };

    customEffect();

    customFunction();
  }, []);

  return (
    <>Hello World</>
  )

}


我尝试添加 isReady 作为第二个 useEffect 参数,但是我的 customFunction 在 customEffect 完成之前运行,然后在 isReady 设置后再次运行。

还尝试使用单独的 useEffect,但似乎仍会在 customEffect 完成之前运行。

4 个答案:

答案 0 :(得分:1)

由于您希望在设置 isReady 状态之后 提示效果运行,并且 isReady 的值无关紧要,因此您可以使用第二个状态值来表示第一个效果和状态更新已完成。

这将触发调用 customFunction 的第二个效果,但您不希望您的组件保持此状态,因为从这里开始,组件重新渲染时仍会满足条件。您需要第三个“状态”来指示已触发第二个效果。在这里你可以使用 React ref 来表明这一点。

export const smallComponent = () => {
  const [readySet, setReadySet] = useState(false);
  const [isReady, setIsReady] = useState(false);
  const customFunctionRunRef = useRef(false);

  useEffect(() => {
    const customEffect = async () => {
      try {
        const response = await get(
          `some-api.com`,
        );

        setReadySet(true); // to trigger second effect callback
        return setIsReady(response); // response can be true or false
      } catch {
        return null;
      }
    };

    customEffect();
  }, []);

  useEffect(() => {
    if (readySet && !customFunctionRunRef.current) {
      // won't run before readySet is true
      // won't run after customFunctionRunRef true
      customFunction();
      customFunctionRunRef.current = true;
    }
  }, [readySet]);

  return (
    <>Hello World</>
  );
}

从@p1uton 借用的更好的解决方案。使用 null isReady 状态来指示 customFunction 不应该被调用,并且使用 ref 来防止它被调用。

export const smallComponent = () => {
  const [isReady, setIsReady] = useState(null);
  const customFunctionRunRef = useRef(false);

  useEffect(() => {
    const customEffect = async () => {
      try {
        const response = await get(
          `some-api.com`,
        );

        return setIsReady(response); // response can be true or false
      } catch {
        return null;
      }
    };

    customEffect();
  }, []);

  useEffect(() => {
    if (isReady !== null && !customFunctionRunRef.current) {
      // won't run before isReady is non-null
      // won't run after customFunctionRunRef true
      customFunction();
      customFunctionRunRef.current = true;
    }
  }, [isReady]);

  return (
    <>Hello World</>
  );
}

答案 1 :(得分:1)

将初始值设置为 null 并按照 Kevin 的建议使用单独的 useEffect(仅不检查 isReady true/false)。

在这种情况下,setIsReady 会将 isReady 从 null 更改为 true/false,并且将调用第二个 useEffect

import customFunction from 'myFile';

export const smallComponent = () => {
    const [isReady, setIsReady] = useState(null);

    useEffect(() => {
        const customEffect = async () => {
            try {
                const response = await get(
                    `some-api.com`,
                );
                return setIsReady(response);
            } catch {
                return null;
            }
        };

        customEffect();
    }, []);

    useEffect(() => {
       if (null === isReady) {
          return;
       }
       customFunction();
    }, [isReady]);

    return (
        <>Hello World</>
    )
}

答案 2 :(得分:0)

我不确定我是否理解正确,但这就是我使用单独的 useEffect 的方式。

import customFunction from 'myFile';

export const smallComponent = () => {
    const [isReady, setIsReady] = useState(false);

    useEffect(() => {
        const customEffect = async () => {
            try {
                const response = await get(
                    `some-api.com`,
                );
                return setIsReady(response);
            } catch {
                return null;
            }
        };

        customEffect();
    }, []);

    useEffect(() => {
        if (!isReady) {
            return;
        }

        customFunction();
    }, [isReady]);

    return (
        <>Hello World</>
    )
}

答案 3 :(得分:0)

您是否尝试过使用此软件包,isMounted

我在我的项目中使用了它。

import React, { useState, useEffect } from 'react';
import useIsMounted from 'ismounted';
import myService from './myService';
import Loading from './Loading';
import ResultsView from './ResultsView';
 
const MySecureComponent = () => {
  const isMounted = useIsMounted();
  const [results, setResults] = useState(null);
 
  useEffect(() => {
    myService.getResults().then(val => {
      if (isMounted.current) {
        setResults(val);
      }
    });
  }, [myService.getResults]);
 
  return results ? <ResultsView results={results} /> : <Loading />;
};
 
export default MySecureComponent;

https://www.npmjs.com/package/ismounted