#reactjs #redux #react-hooks
#reactjs ( реакция ) #сокращение #реагирующие крючки
Вопрос:
Я использую useReducer
хук с контекстом для создания хранилища состояний Redux-ish, поддерживающего промежуточное программное обеспечение.
const Provider = (props: any) => {
const [state, dispatch] = React.useReducer(reducer, {
title: 'Default title',
count: 0,
});
const actionDispatcher = makeActionDispatcher(
dispatch,
applyMiddleware(state, thunkMiddleware, callApiMiddleware, logger),
);
return (
<Context.Provider value={{ ...state, ...actionDispatcher }}>
{props.children}
</Context.Provider>
);
};
Обратите внимание, что я перехожу state
к applyMiddleware
:
const applyMiddleware = (state: {}, ...middlewares: Function[]) =>
function dispatcher(dispatch: Function) {
const middlewareAPI = {
state,
dispatch: (...args) => dispatch(...args),
};
const chain = middlewares.map((middleware) => {
return middleware(middlewareAPI);
});
return compose(...chain)(dispatch);
};
Это работает, но в конечном итоге я хочу иметь возможность работать с асинхронными действиями, поэтому в идеале у меня было бы что-то вроде redux-thunk
:
function thunkMiddleware(store: Store) {
return (next: Function) => (action: any) => {
typeof action === 'function' ? action(next, store.getState) : next(action);
};
}
Учитывая, что промежуточное программное обеспечение thunk будет реагировать на асинхронные действия, в идеале мы могли бы передать функцию для извлечения текущего состояния при необходимости — getState
— вместо того, чтобы быть вынужденными использовать состояние, которое существовало при применении промежуточного программного обеспечения, которое может устареть.
Обычно я бы передал что-то вроде этого:
const getState = () => React.useReducer(reducer, {
title: 'Default title',
count: 0,
})[0];
Но если я передам это промежуточному программному обеспечению для вызова, я получу сообщение об ошибке, указывающее, что я могу вызывать перехваты только из функций React.
Я неправильно проектирую? Я неправильно оборачиваю голову вокруг крючков?
ОБНОВЛЕНИЕ: добавление запрошенной makeActionDispatcher
реализации
export const makeActionDispatcher = (
dispatch: React.Dispatch<any> | undefined,
enhancer?: Function,
): ActionDispatcher => {
const actionDispatcher: { [key: string]: (...args: any) => void } = {};
Object.keys(actionCreators).forEach((key) => {
const creator = actionCreators[key];
actionDispatcher[key] = (...args: any) => {
if (!dispatch) {
throw new Error('ActionDispatcher has not been initialized!');
}
const action = creator(...args);
if (enhancer) {
const enhancedDispatch = enhancer(dispatch);
enhancedDispatch(action);
} else {
dispatch(action);
}
};
});
return actionDispatcher as ActionDispatcher;
};
Комментарии:
1. Что-то вроде github.com/jamesseanwright/roll-your-own-redux/blob/master/src /… ?
2. Как
makeActionDispatcher
выглядит ваш3. @Federkun Глядя на реализацию, кажется, что для любых асинхронных операций также невозможно получить текущее состояние, только состояние, когда отправка была увеличена.
4. добавил @fard — но я не знаю, насколько это уместно для вопроса.
5. Вы пытаетесь использовать перехваты вне контекста react. Хуки разработаны таким образом, что вы можете использовать их только в функциональном компоненте react. В вашем случае, поскольку вам необходимо обрабатывать изменения хранилища вне компонента, вы можете попробовать перенести свое состояние во внешнюю функцию и создать пользовательский хук reactjs.org/docs/hooks-custom.html
Ответ №1:
Используйте useEnhancedReducer
хук, представленный здесь.
Тогда у вас будет что-то вроде.
const [state, dispatch, getState] = useEnahancedReducer(reducer, initState)
Поскольку dispatch
, getState
никогда не изменится, вы можете передать его какому-либо перехватчику, не добавляя их в список зависимостей, или сохранить их где-нибудь еще, чтобы вызывать их извне.
Существует также версия useEnhancedReducer
, которая поддерживает добавление промежуточного программного обеспечения, в том же посте.