#reactjs #typescript #types #context-api
Вопрос:
В упрощенной версии моего приложения я получаю комментарии с сервера, и каждый комментарий имеет массив ответов в качестве свойства. Я хочу нормализовать свое состояние, чтобы у меня были комментарии без ответов как одна часть состояния и ответы как вторая часть состояния.
Когда я создаю комментарий, я отправляю только author
и. body
Когда я получаю комментарий от сервера, я получаю дополнительные свойства вместе с replies
массивом и в состоянии, в котором я хочу иметь комментарии без ответов.
Потому что, если это в моем types.ts
файле, у меня есть 4 разных интерфейса. Один предназначен для нового комментария, другой-для комментария без ответов, который переходит в состояние, третий-для всего комментария, полученного с сервера, и четвертый-только для ответа.
export interface NewComment {
author: string,
body: string
}
export interface CommentWithoutReplies {
id: string,
author: string,
body: string,
postedAt: number,
replies_count: number,
}
export interface Comment {
id: string,
author: string,
body: string,
postedAt: number,
replies_count: number,
replies: Reply[]
}
export interface Reply {
id: string
comment_id: string,
author: string,
body: string,
postedAt: number,
}
Я использую создание файла контекста комментария, который теперь выглядит так и проходит проверку:
import { createContext, useCallback, useState, FC } from "react";
import {Comment, CommentWithoutReplies, CommentContextState, CommentContextDispatch} from "../types/types"
const defaultStateValue: CommentContextState = {
comments: [],
};
const defaultDispatchValue: CommentContextDispatch = {
getComments: () => undefined,
addComment: () => undefined,
};
export const CommentStateContext = createContext<CommentContextState>(defaultStateValue);
export const CommentDispatchContext = createContext<CommentContextDispatch>(defaultDispatchValue);
const CommentProvider: FC = ({children}) => {
const [comments, setComments] = useState<CommentWithoutReplies[]>(defaultStateValue.comments);
const getComments = useCallback(
(data: Comment[]) => {
setComments(() => {
return data.map(comment => {
const {replies, ...commentWithoutReplies} = comment;
return commentWithoutReplies
})
});
},
[setComments]
);
const addComment = useCallback(
(data: CommentWithoutReplies) => {
setComments((currentComments: CommentWithoutReplies[]) => {
return [...currentComments, data];
});
},
[setComments]
);
return (
<CommentDispatchContext.Provider
value={{
getComments,
addComment,
}}
>
<CommentStateContext.Provider
value={{
comments,
}}
>
{children}
</CommentStateContext.Provider>
</CommentDispatchContext.Provider>
);
};
export default CommentProvider;
Есть ли более изящный способ обработки этих типов, чем иметь 4 разных интерфейса?
Ответ №1:
То, что вы сейчас делаете, кажется мне вполне разумным — вы используете несколько отдельных форм данных, поэтому в большинстве случаев вам нужно где-то определить каждую из этих форм. Тем не менее, вы можете сделать вещи менее повторяющимися, не отмечая типы, когда в этом нет необходимости, например, это
setComments((currentComments: CommentWithoutReplies[]) => {
может быть
setComments((currentComments) => {
и вы можете определить типы более кратко, начав с базового типа, который содержит все общие свойства — id
, author
, postedAt
.
type BaseComment {
id: string,
author: string,
postedAt: number,
}
export type CommentWithoutReplies = BaseComment amp; {
body: string,
replies_count: number,
}
export type Comment = BaseComment amp; {
body: string,
replies_count: number,
replies: Reply[]
}
export type Reply = BaseComment amp; {
comment_id: string,
body: string,
}
Не знаю, какой у вас есть другой код, но если типы здесь не импортируются в другое место, вы также можете определить их в самом файле контекста вместо того, чтобы импортировать их.