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;
};