重构 React 组件以供重用
Refactoring a React component for reuse
我正在寻找重构以下 React 组件的建议:
const Block = () => {
const {blockId} = useParams();
const {register, control, handleSubmit} = useForm();
const isNewBlock = typeof blockId === 'undefined';
const [saveBlockMutation] = useSaveBlockMutation();
const [deleteBlockMutation] = useDeleteBlockMutation();
const {data, loading, error} = useGetBlockQuery({
skip: isNewBlock,
variables: {block_id: parseInt(blockId!)}
});
const saveBlock = (input: any /* todo: type it */) => {
saveBlockMutation({variables: {input: input}})
.then(result => {
if (result.data?.saveBlock) {
// show notification
}
})
};
const deleteBlock = (blockId: number) => {
deleteBlockMutation({variables: {block_id: blockId}})
.then(result => {
if (result.data?.deleteBlock) {
// show notification
}
})
}
return (
<LoaderHandler loading={loading} error={error}>
{!loading && (
<>
<Header text={data ? "Block: " + data.block.identifier : "Add Block"}>
<Button onClick={handleSubmit(saveBlock)}>Save</Button>
{!isNewBlock && (<Button onClick={() => deleteBlock(parseInt(blockId!))}>Delete</Button>)}
</Header>
<Form data={data} register={register} control={control}/>
</>
)}
</LoaderHandler>
)
}
这目前工作正常,但我将添加一些其他组件,它们的行为应该完全相同:
- 从 URL
获取一些 ID
- 加载一些数据
- 呈现表单
- 保存突变
- 删除突变
- save/delete 个按钮
我觉得我应该能够将列表中的所有内容提取到更通用的内容中,除了“呈现表单”部分。
我无法确定那个“东西”是什么。也许 HOC 适合这里?我最终会得到类似的东西:
const Block = (props: WithCrudProps) => {
// we only render a form here
}
export default withCRUD(
Block,
{
deleteMutation: DeleteBlockMutation,
saveMutation: SaveBlockMutation,
getQuery: GetBlockQuery,
// etc.
}
);
但感觉很快就会变得一团糟。解决这个问题的“反应方式”是什么?
我认为 withCRUD
很难有一个好的实现,因为这里的所有关系:
// you need param name to extract it from params:
const params = useParams();
const param = params[paramName];
// then you need to convert param to query variables:
const queryVariables = makeQueryVariables(param)
// and you will need more of that for mutations
所以我会推荐自定义挂钩
interface UseCrudParams<T, Vars> {
id?: number;
initialData?: T;
onSave: (vars: Vars) => Promise<void>;
onDelete: () => Promise<void>
}
function useCrud<T, Vars>({
id,
initalData,
onSave,
onDelete,
}: UseCrudParams<T, Vars>): CrudProps { /* ... */}
// and then when you use it you adapt mutations to form
const formProps = useCrud({
id: blockId,
initialData: data,
onSave: variables => saveBlockMutation({ variables }),
onDelete: () => deleteBlockMutation({ variables: { block_id: blockId } }),
})
并为表单布局创建 UI 组件:
function FormLayout({ header, loading, error, onSave, showDelete, onDelete, children }: FormLayoutProps) {
return (
<LoaderHandler loading={loading} error={error}>
{!loading && (
<>
<Header text={header}>
<Button onClick={onSave}>Save</Button>
{showDelete && (<Button onClick={onDelete}>Delete</Button>)}
</Header>
{children}
</>
)}
</LoaderHandler>
)
}
我正在寻找重构以下 React 组件的建议:
const Block = () => {
const {blockId} = useParams();
const {register, control, handleSubmit} = useForm();
const isNewBlock = typeof blockId === 'undefined';
const [saveBlockMutation] = useSaveBlockMutation();
const [deleteBlockMutation] = useDeleteBlockMutation();
const {data, loading, error} = useGetBlockQuery({
skip: isNewBlock,
variables: {block_id: parseInt(blockId!)}
});
const saveBlock = (input: any /* todo: type it */) => {
saveBlockMutation({variables: {input: input}})
.then(result => {
if (result.data?.saveBlock) {
// show notification
}
})
};
const deleteBlock = (blockId: number) => {
deleteBlockMutation({variables: {block_id: blockId}})
.then(result => {
if (result.data?.deleteBlock) {
// show notification
}
})
}
return (
<LoaderHandler loading={loading} error={error}>
{!loading && (
<>
<Header text={data ? "Block: " + data.block.identifier : "Add Block"}>
<Button onClick={handleSubmit(saveBlock)}>Save</Button>
{!isNewBlock && (<Button onClick={() => deleteBlock(parseInt(blockId!))}>Delete</Button>)}
</Header>
<Form data={data} register={register} control={control}/>
</>
)}
</LoaderHandler>
)
}
这目前工作正常,但我将添加一些其他组件,它们的行为应该完全相同:
- 从 URL 获取一些 ID
- 加载一些数据
- 呈现表单
- 保存突变
- 删除突变
- save/delete 个按钮
我觉得我应该能够将列表中的所有内容提取到更通用的内容中,除了“呈现表单”部分。
我无法确定那个“东西”是什么。也许 HOC 适合这里?我最终会得到类似的东西:
const Block = (props: WithCrudProps) => {
// we only render a form here
}
export default withCRUD(
Block,
{
deleteMutation: DeleteBlockMutation,
saveMutation: SaveBlockMutation,
getQuery: GetBlockQuery,
// etc.
}
);
但感觉很快就会变得一团糟。解决这个问题的“反应方式”是什么?
我认为 withCRUD
很难有一个好的实现,因为这里的所有关系:
// you need param name to extract it from params:
const params = useParams();
const param = params[paramName];
// then you need to convert param to query variables:
const queryVariables = makeQueryVariables(param)
// and you will need more of that for mutations
所以我会推荐自定义挂钩
interface UseCrudParams<T, Vars> {
id?: number;
initialData?: T;
onSave: (vars: Vars) => Promise<void>;
onDelete: () => Promise<void>
}
function useCrud<T, Vars>({
id,
initalData,
onSave,
onDelete,
}: UseCrudParams<T, Vars>): CrudProps { /* ... */}
// and then when you use it you adapt mutations to form
const formProps = useCrud({
id: blockId,
initialData: data,
onSave: variables => saveBlockMutation({ variables }),
onDelete: () => deleteBlockMutation({ variables: { block_id: blockId } }),
})
并为表单布局创建 UI 组件:
function FormLayout({ header, loading, error, onSave, showDelete, onDelete, children }: FormLayoutProps) {
return (
<LoaderHandler loading={loading} error={error}>
{!loading && (
<>
<Header text={header}>
<Button onClick={onSave}>Save</Button>
{showDelete && (<Button onClick={onDelete}>Delete</Button>)}
</Header>
{children}
</>
)}
</LoaderHandler>
)
}