En programmation fonctionnelle, toute fonction asynchrone doit être considérée comme un effet secondaire.
Lorsque vous traitez des effets secondaires, vous devez séparer la logique du déclenchement de l'effet secondaire et la logique du résultat de cet effet secondaire (similaire à la saga redux).
En fait, la responsabilité du bouton ne fait que déclencher l'effet secondaire, et la responsabilité de l'effet secondaire consiste à mettre à jour le domaine.
De plus, étant donné que react traite avec des composants, vous devez vous assurer que votre composant est toujours monté avant tout setState
ou après chaque await
cela dépend de vos propres préférences.
pour résoudre ce problème, nous pouvons créer un hook personnalisé useIsMounted
Ce crochet nous permettra de vérifier facilement si le composant est toujours monté.
/**
* check if the component still mounted
*/
export const useIsMounted = () => {
const mountedRef = useRef(false);
const isMounted = useCallback(() => mountedRef.current, []);
useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
};
});
return isMounted;
};
Votre code devrait alors ressembler à ceci
export const MyComponent = ()=> {
const isMounted = useIsMounted();
const [isDoMyAsyncThing, setIsDoMyAsyncThing] = useState(false);
// do my async thing
const doMyAsyncThing = useCallback(async () => {
// do my stuff
},[])
/**
* do my async thing effect
*/
useEffect(() => {
if (isDoMyAsyncThing) {
const effect = async () => {
await doMyAsyncThing();
if (!isMounted()) return;
setIsDoMyAsyncThing(false);
};
effect();
}
}, [isDoMyAsyncThing, isMounted, doMyAsyncThing]);
return (
<div>
<button disabled={isDoMyAsyncThing} onClick={()=> setIsDoMyAsyncThing(true)}>
Do My Thing {isDoMyAsyncThing && "Loading..."}
</button>;
</div>
)
}
Nota: Il est toujours préférable de séparer la logique de votre effet secondaire de la logique qui déclenche l'effet (l'élément useEffect
)
UPDATE :
Au lieu de cette complexité, utilisez simplement useAsync
y useAsyncFn
de la react-use
bibliothèque, c'est beaucoup plus propre et direct.
Exemple :
import {useAsyncFn} from 'react-use';
const Demo = ({url}) => {
const [state, doFetch] = useAsyncFn(async () => {
const response = await fetch(url);
const result = await response.text();
return result
}, [url]);
return (
<div>
{state.loading
? <div>Loading...</div>
: state.error
? <div>Error: {state.error.message}</div>
: <div>Value: {state.value}</div>
}
<button onClick={() => doFetch()}>Start loading</button>
</div>
);
};
0 votes
Je pense qu'il n'y a pas d'intérêt à utiliser les react hooks pour faire cela, vous pouvez juste avoir une fonction normale dans votre composant et l'avoir comme gestionnaire d'événement d'entrée.
0 votes
Que pensez-vous du fait que
render
(donc sfc) ne devrait pas avoir d'effets secondaires ?0 votes
L'utilisation d'un hook rendrait le code beaucoup plus flexible, mais il s'agit d'une mesure temporaire car bientôt React gérera cela en utilisant Suspense, voir robinwieruch.de/react-hooks-fetch-data pour plus d'informations.
0 votes
@dorriz oui oui, avec le temps, du suspense sera ajouté à cette histoire. mais en attendant, faisons comme si suspendre n'existait pas ou n'allait pas exister.