Это интересный случай, который примеры useReducer
не затрагивают. Я не думаю, что редуктор является подходящим местом для асинхронной загрузки. Исходя из мышления Redux, вы, как правило, загружаете данные в другом месте, либо в виде thunk, наблюдаемой (например, redux-observable), либо просто в событии жизненного цикла, таком как componentDidMount
. С новым useReducer
мы могли бы использовать componentDidMount
подход с использованием useEffect
. Ваш эффект может быть примерно таким:
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>
);
}
Также, рабочий пример здесь: https://codesandbox.io/s/r4ml2x864m.
Если вам нужно передать реквизит или состояние вашей функции reloadProfile
, вы можете сделать это, настроив второй аргумент на useEffect
(пустой массив в примере), чтобы он выполнялся только при необходимости. Вам нужно будет либо сравнить с предыдущим значением, либо внедрить какой-либо кэш, чтобы избежать необходимости извлекать его, когда в этом нет необходимости.
Обновление - перезагрузка от ребенка
Если вы хотите иметь возможность перезагрузить дочерний компонент, вы можете сделать это несколькими способами. Первый вариант - передача обратного вызова дочернему компоненту, который инициирует отправку. Это может быть сделано с помощью провайдера контекста или компонента prop. Поскольку вы уже используете провайдер контекста, вот пример этого метода:
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>
);
}
Если вы действительно хотите использовать функцию диспетчеризации вместо явного обратного вызова, вы можете сделать это, поместив диспетчер в функцию более высокого порядка, которая обрабатывает специальные действия, которые обрабатывались бы промежуточным ПО в мир Redux. Вот пример этого. Обратите внимание, что вместо передачи profileR
непосредственно в провайдер контекста, мы передаем пользовательский, который действует как промежуточное ПО, перехватывая специальные действия, которые не имеют значения для редуктора.
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>
);
}