Les hooks React ne peuvent être utilisés que dans le corps de votre composant et non dans une autre fonction. executeFetch
retourne lui-même { data, error, isPending }
et cela en fait un crochet imbriqué, donc vous ne pouvez pas l'utiliser à l'intérieur de votre handleSubmit.
useFetch
retourne déjà { data, error, isPending, executeFetch }
pour que executeFetch n'ait pas besoin de revenir à nouveau. Vous pouvez accéder à toutes ces données à partir du hook useFetch. Lorsque vous appelez executeFetch data dans votre composant, data, error et isPending seront mis à jour par setState
qui fera en sorte que votre hook renvoie un nouvel ensemble de valeurs pour toutes les valeurs mises à jour.
export default function useFetch(url, options, { immediate }) {
const [data, setData] = useState(null)
const [error, setError] = useState(null)
const [isPending, setIsPending] = useState(false)
const executeFetch = useCallback(async () => {
setIsPending(true)
setData(null)
setError(null)
await fetch(url, options)
.then((response) => response.json())
.then((response) => setData(response))
.catch((err) => setError(err))
.finally(() => setIsPending(false))
}, [url, options, data, error, isPending])
useEffect(() => {
if (immediate) {
executeFetch()
}
}, [executeFetch, immediate])
return { data, error, isPending, executeFetch }
}
export default function SignupModal({ closeModal }) {
const { executeFetch, data, error, isPending } = useFetch(url, {options},
{ immediate: false }
)
async function handleSubmit(evt) {
evt.preventDefault()
await executeFetch()
}
...
// Example in your return function
{error != null && <Error />}
<Button state={isPending ? 'processing' : 'normal'}
}
Mis à jour sur la base du commentaire
Si vous avez besoin d'accéder à des données ou à une erreur dans votre fonction handleSubmit, vous devrez renvoyer la réponse/erreur de la promesse dans votre hook, de sorte que vous devriez pouvoir accéder aux données/erreurs dans votre handleSubmit également.
Je recommande également de passer les options ou toute autre donnée variable susceptible d'être modifiée avant que l'utilisateur ne déclenche handleSubmit à l'executeFetch en tant qu'argument afin que l'executeFetch puisse toujours obtenir les dernières données.
CodeSandBox Exemple 1
CodeSandBox Exemple 2
const useFetch = url => {
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const [data, setData] = useState(null);
const executeFetch = useCallback(
// Here you will access to the latest updated options.
async ({ options }) => {
setIsPending(true);
setError(null);
return await fetch(url, options)
.then(response => response.json())
.then(response => {
setData(response);
return response;
})
.catch(err => {
setError(err.message)
return err;
})
.finally(() => setIsPending(false));
},
[url, setIsPending, setError]
);
return { data, error, isPending, executeFetch }
};
const { data, executeFetch, error, isPending } = useFetch("URL");
const handleSubmit = useCallback(async (event) => {
event.preventDefault();
// I am passing hardcoded { id: 1 } as an argument. This can
// be a value from the state ~ user's input depending on your
// application's logic.
await executeFetch({ id: 1 }).then(response => {
// Here you will access to
// data or error from promise.
console.log('RESPONSE: ', response);
})
}, [executeFetch]);
Une autre recommandation est de ne pas passer un booléen pour déclencher executeFetch immédiatement dans votre hook, c'est à l'appelant de décider s'il faut exécuter executeFetch immédiatement ou non.
const { executeFetch, ... } = useFetch(....);
// you can call it immediately after setting the hook if you ever needed
await executeFetch()