view = fn(props, state, context)
1. 是否已明确入参?
- 入参
interface、type 是否已经编写?哪些参数需要必填?需要外部传入哪些参数(属性、事件)?
interface IGenericProps<T> {
value: T;
valueProp: string;
labelProp: string;
onChange?: (val: T) => void;
}
interface IForwardMethod {
onEvent: () => void;
}
...
useImperativeHandle(outerRef, () => {
return {
onEvent: () => console.log('触发了onEvent'),
};
});
- 是否需要解构?解构的优劣势?参数是否需要有默认值,默认值为啥?(注意:默认值只对undefined生效,null无效)(请遵循使用方便原则)
const { value, checked, onChange, element, elementType, modeList = [DefaultModeEnum.CUSTOM], onBlur } = props;
const { data, type } = element;
const { formItemSettings } = data;
const { defaultMode = DefaultModeEnum.CUSTOM } = formItemSettings || {};
- 是否需要使用插槽?插槽是否有动态参数?是否需要传递组件引用?
const renderEditable = () => {
return (
<Input
ref={innerRef}
...
defaultValue={defaultValue}
value={validVal}
maxLength={maxLength}
onBlur={handleBlur}
onChange={handleChange}
/>
);
};
<ToggleEditComp
editRef={innerRef}
readonly={readonly}
autoToggle={autoToggle}
editableComp={renderEditable()}
readonlyComp={renderReadonly()}
trigger={trigger}
/>
import DatePickerComp from '../render/DatePickerComp';
...
<SettingsDefaultValue
element={element}
elementType={DatePickerComp}
modeList={[DefaultModeEnum.CURRENT, DefaultModeEnum.CUSTOM]}
/>
2. 通用还是业务?
2.1 通用组件
- 不应存在任何业务属性,props 都应该只服务于渲染。所有的业务判断交于外层,内部只应该接收
enum 或 boolean;
- 若为泛型组件,需要复写外层
.d.ts 文件;否则 ts 提示错误
import React from 'react';
declare module 'react' {
function memo<A, B>(Component: (props: A) => B): (props: A) => ReactElement | null;
function forwardRef<T, P = {}>(
render: (props: P, ref: React.Ref<T>) => React.ReactElement | null
): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
}
- 不要使用
redux 等【注入状态】来共享数据,若需要数据,一律通过 props 传递
- 若为大组件中的小组件,希望共享数据,可以酌情使用
useContext 或者 Zustand来共享数据,让其支持多实例;
2.2 业务组件
- 是否是路由组件,非路由组件尽量不适用
useParams 等方式获取路由参数,而是通过 props 传递;
- 使用【状态管理】,比如
redux 时,注意缓存控制,提高性能。注意只有需要共享的数据放入 redux,不要把 redux 当仓库
3. 频繁还是不频繁?
3.1 频繁调用
- 使用
useMemoizedFn + React.memo + react-fast-compare
- 弹框使用 visible + useEffect 监听 value 变化
- 考虑单例或使用状态管理缓存不变量(比如区域组件)
3.2 不频繁调用
- 没必要使用
useMemoizedFn + React.memo
- 弹框使用销毁模式,每一次都是重新创建,从而减少监听
4. 样式策略?
- 使用
styled-component 还是使用 css-modules还是纯 namespace
- 如何覆盖三方库样式
- 动态样式,style 和
classNames
5. api 策略?
- 抽离 api 函数,方便使用
- 定义 req 和 res 对应的 interface,便于接口调试。也方便后续开发者理解前后端交互数据结构
interface IQueryUserReqAttr {
pageIndex: number;
pageLen: number;
fuzzyField?: string;
}
interface IQueryUserDataReqAttr extends IQueryUserReqAttr {
inUserIds?: number[];
notInUserIds?: number[];
}
export async function getUserList(reqData: IQueryUserDataReqAttr): Promise<ICommonRes<IPageInfoRes<IUserSelectAttr>>> {
const { pageIndex, pageLen, ...restData } = reqData;
return axiosIns('/arm/sp/getUserList', {
method: ApiMethodType.POST,
params: {
pageIndex,
pageLen,
},
data: restData,
});
}
- 统一调用单例,比如 axios。以及其内部的拦截器(request 和 response)
const defaultRequsetInterceptor = axiosIns.interceptors.request.use(
(config) => {
...
},
(error) => {
...
}
);
const defaultResponseInterceptor = axiosIns.interceptors.response.use(
(response) => {
...
},
(error) => {
...
}
);
6. 能否复用?
- 【上下文无关】【纯函数】的抽离
util、【有上下文或状态】【生命周期】的抽离 hooks
- utils 中:format、解构转换
- hooks 中:数据管理、数据获取(带 loading)
- 组件块的复用,比如按钮组
- 通用底层组件,减少编写重复的 UI
7. 写注释了么?
- 防止业务复杂,自己都忘了逻辑
- 可以快速提升他人理解的速度
- 便于后期优化、拆分