在业务中部分逻辑需要抽取到高阶组件里面去,而编写 高阶组件的范型就需要我们格外的注意,否则业务组件没有提示
- 在 React 中使用多语言方案,通常采用
react-i18next这个npm包,我们在业务中一般像如下这种方式使用
// 业务 A 组件
import { useTranslation } from "react-i18next";
const Company = () => {
const { t } = useTranslation();
return (
<div>
{t('xxx.yy.zzz')}
</div>
);
};
export default Company;
-
正常使用
react-i18next确实没什么问题,但是我们需要在每个使用到 多语言的地方 重复import, 重复const { t } = useTranslation();这样才能够正常拿到t。 这也不是不可以,但是做了太多重复的事情。基于 D.R.Y 原则,可以把这里封装成高阶组件 (高阶组件内容本身好写, 高阶组件范型没那么好写) -
这里的高阶组件类型就有问题
- 多语言 高阶组件
// 定义的 多语言 高阶组件
import { useTranslation } from "react-i18next";
import type { ComponentType } from "react";
export default function withWrapperCom<TProps>(
WrappendCom: ComponentType<TProps & WithWrapperComType>,
) {
return (props: TProps) => {
const { t } = useTranslation();
return (
<WrappendCom
t={t}
{...props}
/>
);
};
}
// 定义的 高阶组件 范型
export type WithWrapperComType = {
t: TFunction<"translation", undefined>;
};
使用高阶组件
import withWrapperCom, { WithWrapperComType } from '@/components/withWrapperCom'
type TypeCompany: object
const Company = (props: TypeCompany & WithWrapperComType) => {
const { t } = props.t;
return (
<div>
{t('xxx.yy.zzz')}
</div>
);
};
export default withWrapperCom(Company);
基于上面的高阶组件,虽然已经完成了功能,并且可以正常运行,但是 使用方 每次在使用 高阶组件都需要导入相应的范型WithWrapperComType,比较麻烦, 且不利于使用 babel plugin 进行自动化 动态插入 高阶组件(因为 WithWrapperComType 是基于 compile time,不是基于 run time)
-
改善高阶组件
- 定义的高阶组件如下
- 高阶组件的
WithWrapperComType单独 export 出去,方便后续有特殊业务可以单独import使用, 正常情况下 使用HideComPropsWithChildren即可满足业务需求
// 高阶组件
import { useTranslation } from "react-i18next";
import type { ComponentType } from "react";
export default function withWrapperCom<TProps>(
WrappendCom: ComponentType<TProps & WithWrapperComType>,
) {
return (props: TProps) => {
const { t } = useTranslation();
return (
<WrappendCom
t={t}
{...props}
/>
);
};
}
// 定义的 高阶组件 范型
export type WithWrapperComType = {
t: TFunction<"translation", undefined>;
};
- 定义的 全局
globalWithHideCom.d.ts文件 (此文件需要加入到tsconfig.json里面)
//globalWithHideCom.d.ts
import type { PropsWithChildren } from "react";
import type { WithWrapperComType } from "@/components/withWrapperCom";
declare global {
type HideComPropsWithChildren<T> = PropsWithChildren<T & WithWrapperComType>;
}
- 业务方使用
// 业务 A 组件
import withWrapperCom from '@/components/withWrapperCom'
type TypeCompany: object
const Company = (props: HideComPropsWithChildren<TypeCompany>) => {
const { t } = props.t;
return (
<div>
{t('xxx.yy.zzz')}
</div>
);
};
export default withWrapperCom(Company);
最终:
- 业务方在使用的时候可以直接使用
withWrapperCom包装业务组件, 且 高阶组件的 范型也 不需要每次都导入 (这里定义了HideComPropsWithChildren范型,使用时直接把 业务组件的范型传入HideComPropsWithChildren即可) - 就本
case来说,TProps才是真正业务传递的范型。定义的高阶组件的参数WrappendCom在实际调用的时候有传递参数t, 所以定义的业务组件A的范型里面有t(定义 A组件的范型需要使用HideComPropsWithChildren进行传递), 对应的 定义的高阶组件的参数WrappendCom的范型 也需要传递t。(这里需要明确清楚知道 定义的组件 和 实际调用的组件 不一致,且范型也不一致, 什么时候调用 真实的业务组件,什么时候调用被高阶之后的组件) - 使用
babel plugin自动注入 自定义高阶组件链接