64 votes

Comment envoyer une demande en cliquant sur React Hooks ?

Comment envoyer une requête http sur un clic de bouton avec react hooks ? Ou, d'ailleurs, comment faire n'importe quel effet secondaire sur un clic de bouton ?

Ce que je vois jusqu'à présent, c'est d'avoir quelque chose d'"indirect" comme.. :

export default = () => {
  const [sendRequest, setSendRequest] = useState(false);

  useEffect(() => {
    if(sendRequest){
       //send the request
       setSendRequest(false);
    }
  },
  [sendRequest]);

  return (
    <input type="button" disabled={sendRequest} onClick={() => setSendRequest(true)}
  );
}

Est-ce la bonne méthode ou existe-t-il un autre modèle ?

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.

50voto

DoXicK Points 2091
export default () => {
  const [isSending, setIsSending] = useState(false)
  const sendRequest = useCallback(async () => {
    // don't send again while we are sending
    if (isSending) return
    // update state
    setIsSending(true)
    // send the actual request
    await API.sendRequest()
    // once the request is sent, update state again
    setIsSending(false)
  }, [isSending]) // update the callback if the state changes

  return (
    <input type="button" disabled={isSending} onClick={sendRequest} />
  )
}

voici ce qu'il faut faire lorsque vous voulez envoyer une requête au clic et désactiver le bouton pendant l'envoi.

mettre à jour :

@tkd_aj a fait remarquer que cela pourrait donner un avertissement : " Impossible d'effectuer une mise à jour de l'état React sur un composant non monté ". C'est un no-op, mais cela indique une fuite de mémoire dans votre application. Pour corriger, annulez toutes les souscriptions et les tâches asynchrones dans une fonction de nettoyage useEffect."

En fait, la demande est toujours en cours de traitement, alors qu'entre-temps votre composant se démonte. Il essaie alors de setIsSending (un setState) sur un composant non monté.

export default () => {
  const [isSending, setIsSending] = useState(false)
  const isMounted = useRef(true)

  // set isMounted to false when we unmount the component
  useEffect(() => {
    return () => {
      isMounted.current = false
    }
  }, [])

  const sendRequest = useCallback(async () => {
    // don't send again while we are sending
    if (isSending) return
    // update state
    setIsSending(true)
    // send the actual request
    await API.sendRequest()
    // once the request is sent, update state again
    if (isMounted.current) // only update if we are still mounted
      setIsSending(false)
  }, [isSending]) // update the callback if the state changes

  return (
    <input type="button" disabled={isSending} onClick={sendRequest} />
  )
}

25voto

Shubham Khatri Points 67350

Vous n'avez pas besoin d'un effet pour envoyer une requête lors d'un clic sur un bouton, mais simplement d'une méthode de gestion que vous pouvez optimiser en utilisant la fonction useCallback méthode

const App = (props) => {
   //define you app state here
   const fetchRequest = useCallback(() => {
       // Api request here
   }, [add dependent variables here]);

  return (
    <input type="button" disabled={sendRequest} onClick={fetchRequest}
  );
}

Demande de suivi utilisant une variable avec useEffect n'est pas un modèle correct car vous pouvez définir l'état pour appeler l'API en utilisant useEffect, mais un rendu supplémentaire dû à un autre changement fera tourner la requête en boucle.

0 votes

@deezg content d'avoir aidé :-)

4voto

Tholle Points 28692

Vous pouvez récupérer des données à la suite d'un changement d'état, comme vous l'avez fait dans votre question, mais vous pouvez également obtenir les données directement dans le gestionnaire de clic, comme vous en avez l'habitude dans un composant de classe.

Exemple

const { useState } = React;

function getData() {
  return new Promise(resolve => setTimeout(() => resolve(Math.random()), 1000))
}

function App() {
  const [data, setData] = useState(0)

  function onClick() {
    getData().then(setData)
  }

  return (
    <div>
      <button onClick={onClick}>Get data</button>
      <div>{data}</div>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>

<div id="root"></div>

4voto

fareed namrouti Points 3830

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

2voto

Teneff Points 6753

Vous pouvez définir le booléen dans l'état comme vous l'avez fait et, une fois que vous avez déclenché la demande, lui donner la valeur suivante true et lorsque vous recevrez la réponse, remettez-la à la valeur false :

const [requestSent, setRequestSent] = useState(false);

const sendRequest = () => {
  setRequestSent(true);
  fetch().then(() => setRequestSent(false));
};

Exemple de travail

0 votes

Je peux très bien me tromper, mais sfc n'est-il pas censé être sans effets secondaires ? je sais que cela fonctionne, mais cela introduit des effets secondaires sans l'utilisation réelle de crochets de type effets. est-ce l'approche recommandée ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X