useLogic
useLogic
предназначен для реализации логики фичи, сильно зацепленной на используемую react-библиотеку и фичи самого react.
Если логику можно реализовать без использования хука, то стоит отдать предпочтение UIStore.
├── PaymentForm/
| ├── UIStore/
| ├── useLogic/
| | |── utils/
| | |── hooks/
| | |── useLogic.ts
| | |── useLogic.test.ts
| | |── constants.ts
| | |── enums.ts
| | |── types.ts
| | └── index.ts
| ├── PaymentForm.tsx
| └── index.ts
useLogic - единственная точка входа данных для компонента
Если в фиче есть useLogic
, то только из него должны потребляться данные для компонента.
Даже если в фиче используется UIStore или другие stores:
const Card = (props: Props) => {
const [uiStore] = useState(() => createUIStore(props));
const { fullName, isShowDescription, description } = useLogic(uiStore);
return (
<Wrapper>
<Typography>{fullName}</Typography>
{isShowDescription && <Typography>{description}</Typography>}
</Wrapper>
);
};
export const useLogic = (store: UIStore) => {
const isShowDescription = useEndScroll();
return {
isShowDescription,
fullName: store.fullName,
description: store.fullName,
};
};
Hook должен возвращать абстрактный интерфейс, с которым работает компонент.
Благодаря этой абстракции появляется возможность изменить инструмент реализации логики, при этом оставить выходной интерфейс и ui неизменными.
Взаимодействие с UIStore
useLogic
должен принимать UIStore
и другие stores параметром по ссылке для возможности более простого тестирования:
export const useLogic = (store: UIStore) => {
const isShowDescription = useEndScroll();
return {
isShowDescription,
fullName: store.fullName,
description: store.fullName,
};
};
Инициализация UIStore
UIStore
при использовании useLogic
инициализируется в компоненте и передается в useLogic
:
const Card = (props: Props) => {
const [uiStore] = useState(createUIStore);
const { fullName, isShowDescription, description } = useLogic(uiStore);
return (
<Wrapper>
<Typography>{fullName}</Typography>
{isShowDescription && <Typography>{description}</Typography>}
</Wrapper>
);
};
Зависимости
UIStore
не должен зависеть от useLogic
:
Типы должны импортироваться из UIStore
и useLogic
.
А в свою очередь компонент для формирования своих props может использовать типы как из useLogic
, так и из UIStore
.
Разделение зон ответственности между UIStore и useLogic
Зона ответственности store
- Работа с данными. Взаимодействие с слоем
data
- Форматирование данных для отображения, если эти данные не завязаны на изменение state формы
Зона ответственности hooks
Описание типов формы
useLogic
должен содержать типы формы:
export type BookFormValues = {
name: string;
genre: BookRepositoryDTO.GenreDTO;
pageCount: string;
author: AdministrationRepositoryDTO.CreateBookInputDTO['author'];
coAuthor?: AdministrationRepositoryDTO.CreateBookInputDTO['coAuthor'];
isPresentCoAuthor: boolean;
};
Валидация формы
useForm
должен взаимодействовать с валидацией формы:
const validationSchema = v.object<BookFormValues>({
name: v.string(),
genre: v.object<BookFormValues['genre']>({
id: v.string(),
name: v.string(),
description: v.optional(v.string()),
}),
pageCount: v.number(),
author: v.object<BookFormValues['author']>({
name: v.string(),
surname: v.string(),
}),
isPresentCoAuthor: v.optional(v.boolean()),
coAuthor: v.when({
is: (_, ctx) => Boolean(ctx.values?.isPresentCoAuthor),
then: v.object<BookFormValues['author']>({
name: v.string(),
surname: v.string(),
}),
otherwise: v.any(),
}),
});
export const useLogic = () => {
const form = useForm<BookFormValues>({ validationSchema });
...
};
Инициализация формы с необходимыми параметрами
export const useLogic = (): Result => {
const form = useForm<BookFormValues>({ validationSchema });
...
};
Подписка на изменение полей и state формы
export const useLogic = (
store: UIStore,
{ onSubmit }: Params,
): Result => {
const form = useForm<BookFormValues>({ validationSchema });
const isPresentCoAuthor = form.watch('isPresentCoAuthor');
const name = form.watch('name');
useEffect(() => {
store.findBook(name);
}, [name]);
return { form, isPresentCoAuthor, submit: form.handleSubmit(onSubmit) };
};
Формирование данных для отображения
Если данные для отображения завязаны на изменение state формы, то логика форматир ования помещается в хук:
export const useLogic = (): Returned => {
const { watch } = useBookFormContext();
const { name, author } = watch();
return {
name,
authorFullName: `${author.name} ${author.surname}`,
};
};
При использовании useLogic в компоненте не должно оставаться никакой логики, кроме инициализации
В useLogic
переносятся из компонента:
- Подвязка на
mount
,unmount
- Создание
ref
- Обработка событий
Style guide
Тестирование
Тестировать store и hook необходимо отдельно.