Home > Software design >  Stop invoking custom hook on first render
Stop invoking custom hook on first render

Time:09-12

I started having fun with custom hooks recently. I am mostly using them to fetch from api. The thing is that since I cannot really put useFetchLink inside of functions or useEffect i dont know how to prevent it from fetching after website first render. I could put some ifs in the hook but isn't there any other way?

***component*** 

export default function LinkShortener({ setLinkArr }) {
  const [nextLink, setNextLink] = useState();
  const inputRef = useRef(null);

  const handleClick = () => {
    setNextLink(inputRef.current.value);
  };

  const { shortLink, loading, error } = useFetchLink(nextLink);

  useEffect(() => {
    setLinkArr((prev) => [
      ...prev,
      {
        id: prev.length === 0 ? 1 : prev[prev.length - 1].id   1,
        long: nextLink,
        short: shortLink,
      },
    ]);
    inputRef.current.value = "";
  }, [shortLink, error]);

  return (
    <LinkShortenerContainer>
      <InputContainer>
        <LinkInput ref={inputRef} type="text" />
      </InputContainer>
      <Button
        size={buttonSize.medium}
        text={
          loading ? (
            <Loader />
          ) : (
            <FormattedMessage
              id="linkShortener.shortenItBtn"
              defaultMessage="Shorten It !"
            />
          )
        }
        onClick={handleClick}
      ></Button>
    </LinkShortenerContainer>
  );
}
***hook***
const useFetchLink = (linkToShorten) => {
  const [shortLink, setShortLink] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");

  const fetchLink = async () => {
    setLoading(true);
    try {
      const response = await fetch(
        `https://api.shrtco.de/v2/shorten?url=${linkToShorten}`
      );
      if (response.ok) {
        const data = await response.json();
        setShortLink(data.result.short_link);
      } else {
        throw response.status;
      }
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchLink(linkToShorten);
  }, [linkToShorten]);

  const value = { shortLink, loading, error };
  return value;
};```

CodePudding user response:

Why not using directly fetchLink function and calling it whenever you need inside the component? I would change the hook in this way without useEffect inside

const useFetchLink = (linkToShorten) => {
  const [shortLink, setShortLink] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");

  const fetchLink = async () => {
    setLoading(true);
    try {
      const response = await fetch(
        `https://api.shrtco.de/v2/shorten?url=${linkToShorten}`
      );
      if (response.ok) {
        const data = await response.json();
        setShortLink(data.result.short_link);
      } else {
        throw response.status;
      }
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  };

  const value = { shortLink, loading, error, fetchLink };
  return value;
};

CodePudding user response:

Generally speaking - the standard way to avoid useEffect from running of 1st render is to use a boolean ref initialized with false, and toggled to true after first render - see this answer.

However, un your case, you don't want to call the function if linkToShorten is empty, even if it's not the 1st render, so use an if inside useEffect.

const useFetchLink = (linkToShorten) => {
  const [shortLink, setShortLink] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");

  const fetchLink = useCallback(async (linkToShorten) => {
    setLoading(true);
    try {
      const response = await fetch(
        `https://api.shrtco.de/v2/shorten?url=${linkToShorten}`
      );
      if (response.ok) {
        const data = await response.json();
        setShortLink(data.result.short_link);
      } else {
        throw response.status;
      }
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    if(linkToShorten) fetchLink(linkToShorten);
  }, [fetchLink, linkToShorten]);

  const value = { shortLink, loading, error };
  return value;
};
  • Related