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 }
}
}
}
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.
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>
);
}
Exemple de liste de tâches utilisant async useReducer Ne mettez pas de fonctions asynchrones dans un réducteur.