RTK для использованияReducer Hook: руководство
23 ноября 2022 г.Если внутри вашего приложения вы уже используете управление состоянием Redux для централизованного доступа к данным и для некоторых фрагментов данных, нет необходимости хранить их в общем хранилище. Например, случай с формой, когда она обрабатывается в одном детерминированном компоненте.
Будет бесполезно хранить данные в глобальном состоянии и изменять данные только из одного куска приложения, которое уже логически объединено, я имею в виду компонент формы и его дочерние элементы. В этом сценарии хук useReducer
будет красиво смотреться на своем месте.
Хук useReducer
— это способ хранения и управления данными в хранилище для конкретного компонента и его дочерних элементов. Итак, в базовом сценарии, когда нужды в редуксе нет, я буду использовать сам хук без тяжелых библиотек только для маленькой фичи.
const [type, dispatch] = useReducer(reducer, initializer(editedType));
Для каждого действия потребуется написать тип действия и полезную нагрузку. Он занимает довольно много строк кода в файле только для аннотации действий и их вызова.
interface ChangeNameAction {
type: 'CHANGE_NAME';
name: string;
}
Чтобы держать весь редьюсер в одном месте, я создам еще один хук и верну обратно только те функции, которые будут отправлять в нем требуемые действия для изменения состояния. И хук вернет состояние и функции для изменения состояния. Затем функция смены имени:
const setName = (name: string) => {
dispatch({ type: 'CHANGE_NAME', name });
};
Тогда функция редуктора будет иметь стандартную реализацию switch-case; Я обернул его функцией product
из библиотеки Immer. Это чтобы избежать ошибок, совладав с полным состоянием. Но да, в самом оригинальном редюсере, где объект состояния без множественных вложенных объектов, я буду использовать только оператор распространения.
const reducer: Reducer<TypeDraft, TypeEditorAction> = produce(
(draft: TypeDraft, action: CustomTypeEditorAction) => {
switch (action.type) {
case 'CHANGE_NAME':
draft.name = action.name;
break;
...
default:
}
}
);
И новый хук будет таким:
export function useTypeEditor(editedType?: EditedType) {
const [type, dispatch] = useReducer(reducer, initializer(editedType));
const setName = (name: string) => {
dispatch({ type: 'CHANGE_NAME', name });
};
return {
type,
setName
};
}
Поскольку редюсер — это просто чистая функция, вместо того, чтобы создавать редюсер вручную и оборачивать его в функцию производства Immer, гораздо удобнее использовать функцию RTK createReducer или createSlice для useReducer хук.
const reducer = createReducer(initialState, builder =>
builder
.addCase(changeNameAction, (state, { payload }) => {
state.name = payload;
})
);
Действия также создаются из функции createAction
, а не описываются через интерфейс.
const name = 'typeForm';
const changeNameAction = createAction<string>(`${name}/changeName`);
Это будет тот же редуктор, что и в результате функции product
, или просто чистая функция-редуктор, но в более приличном виде. Мне не нужно помнить, как правильно обрабатывать действие. И в конце для компонента это будет еще один хук, который вы можете просто использовать.
const TypeEditorForm: React.FC = () => {
const editor = useTypeEditor(type);
...
return (<>...</>)
}
Кстати, здесь type
и initializer
, чтобы правильно установить начальное состояние, например, если тип уже был создан, ему нужно инициализировать состояние из него. р>
Фото Стива Джонсона на Unsplash
Оригинал