web-dev-qa-db-fra.com

Réagir avec l'extraction de données asynchrones useReducer

J'essaie d'extraire des données avec la nouvelle API use useReducer de Reaction et je suis resté sur la scène où je dois le récupérer en mode asynchrone. Je ne sais pas comment: /

Comment placer la récupération de données dans une instruction switch ou ce n'est pas une façon dont cela devrait être fait?

import React from 'react'

const ProfileContext = React.createContext()

const initialState = {
  data: false
}

let reducer = async (state, action) => {
  switch (action.type) {
    case 'unload':
      return initialState
    case 'reload':
      return { data: reloadProfile() } //how to do it???
  }
}


const reloadProfile = async () => {
  try {
    let profileData = await fetch('/profile')
    profileData = await profileData.json()

    return profileData
  } catch (error) {
    console.log(error)
  }
}

function ProfileContextProvider(props) {
  let [profile, profileR] = React.useReducer(reducer, initialState)

  return (
    <ProfileContext.Provider value={{ profile, profileR }}>
      {props.children}
    </ProfileContext.Provider>
  )
}

export { ProfileContext, ProfileContextProvider }

J'essayais de le faire comme ça, mais ça ne marche pas avec async;

let reducer = async (state, action) => {
  switch (action.type) {
    case 'unload':
      return initialState
    case 'reload': {
      return await { data: 2 }
    }
  }
}
2
RTW

Il s'agit d'un cas intéressant que les exemples useReducer ne traitent pas. Je ne pense pas que le réducteur est le bon endroit pour charger de manière asynchrone. Venant d'un état d'esprit Redux, vous chargeriez généralement les données ailleurs, que ce soit dans un thunk, un observable (par exemple, redux-observable) ou tout simplement dans un événement de cycle de vie comme componentDidMount. Avec la nouvelle useReducer, nous pourrions utiliser l’approche componentDidMount en utilisant useEffect. Votre effet peut être quelque chose comme ce qui suit:

function ProfileContextProvider(props) {
  let [profile, profileR] = React.useReducer(reducer, initialState);

  useEffect(() => {
    reloadProfile().then((profileData) => {
      profileR({
        type: "profileReady",
        payload: profileData
      });
    });
  }, []); // The empty array causes this effect to only run on mount

  return (
    <ProfileContext.Provider value={{ profile, profileR }}>
      {props.children}
    </ProfileContext.Provider>
  );
}

Aussi, exemple de travail ici: https://codesandbox.io/s/r4ml2x864m .

Si vous devez transmettre un accessoire ou un état à votre fonction reloadProfile, vous pouvez le faire en ajustant le deuxième argument à useEffect (le tableau vide dans l'exemple) afin qu'il ne s'exécute que lorsque cela est nécessaire. Vous devez vérifier la valeur précédente ou implémenter une sorte de cache pour éviter toute extraction inutile.

Mise à jour - Recharger de l'enfant

Si vous souhaitez pouvoir recharger à partir d'un composant enfant, vous pouvez le faire de plusieurs manières. La première option consiste à envoyer un rappel au composant enfant qui déclenchera la répartition. Cela peut être effectué via le fournisseur de contexte ou un accessoire de composant. Puisque vous utilisez déjà le fournisseur de contexte, voici un exemple de cette méthode:

function ProfileContextProvider(props) {
  let [profile, profileR] = React.useReducer(reducer, initialState);

  const onReloadNeeded = useCallback(async () => {
    const profileData = await reloadProfile();
    profileR({
      type: "profileReady",
      payload: profileData
    });
  }, []); // The empty array causes this callback to only be created once per component instance

  useEffect(() => {
    onReloadNeeded();
  }, []); // The empty array causes this effect to only run on mount

  return (
    <ProfileContext.Provider value={{ onReloadNeeded, profile }}>
      {props.children}
    </ProfileContext.Provider>
  );
}

Si vous vraiment voulez utiliser la fonction de dispatch au lieu d'un rappel explicite, vous pouvez le faire en encapsulant le dispatch dans une fonction d'ordre supérieur qui gère les actions spéciales qui auraient été gérées par le middleware dans le monde Redux. Voici un exemple de cela. Notez qu'au lieu de transmettre profileR directement au fournisseur de contexte, nous transmettons le fournisseur personnalisé qui agit comme un middleware, en interceptant des actions spéciales dont le réducteur ne se soucie pas.

function ProfileContextProvider(props) {
  let [profile, profileR] = React.useReducer(reducer, initialState);

  const customDispatch= useCallback(async (action) => {
    switch (action.type) {
      case "reload": {
        const profileData = await reloadProfile();
        profileR({
          type: "profileReady",
          payload: profileData
        });
        break;
      }
      default:
        // Not a special case, dispatch the action
        profileR(action);
    }
  }, []); // The empty array causes this callback to only be created once per component instance

  return (
    <ProfileContext.Provider value={{ profile, profileR: customDispatch }}>
      {props.children}
    </ProfileContext.Provider>
  );
}
1
Tyler

Exemple de liste de tâches utilisant async useReducer Ne mettez pas de fonctions asynchrones dans un réducteur. 

https://codesandbox.io/s/zr3mx12zzx

0
GroteSmurf