前言
最近开始学习React,跟着Kent学,有很多干货,这里分享Rect中的一个设计模式方法重写或者是控制反转IoC,就是用户可以根据API提供的方法重写某些方法以满足设计需求,这样也满足了对修改封闭,对扩展开放原则
一、background
用户的需求是时刻变化的,之前提供的API可能不完全cover用户的需求,这时候就需要给API做扩展;具体的例子比如是reducer给接受的action中的方法需要改变,不应该修改原有的reducer的action方法,而是让用户本省在原有的方法上做一定的扩展,下面有根据题的例子分析方法重写,或者说控制反转的应用场景
二、设计
2.1 API
原来的reducer是toggleReducer,有TOGGLE的action,但是新的组件使用该reducer的时候,有新的需求也就是需要判断toggle的次数,如果toggle一定次数,会保持state为on,也就是禁用了user toggle的行为;除了该需求,其它action不变
于是,toggleReducer需要重复使用,知识修改了其中TOGGLE action的行为, 代码如下
function toggleStateReducer(state, action) {
if (action.type === actionTypes.TOGGLE && timesClicked >= 4) {
return {on: state.on}
}
return toggleReducer(state, action)
}
2.2 控制反转
上面只是重写了toggleStateReducer的一个方法,但是使用组件的用户怎么才能调用toggleStateReducer呢?
重点就是useToggle的方法需要让用户传入reduer,方便用户自定义新的reduer,比如toggleStateReducer;如果用户没有传递reducer,默认是toggleReducer,但是如果user在useToggle中传入了reducer,新的reducer就会被useToggle接收到,这样useToggle返回的就是toggleStateReducer对应的context了
function useToggle({initialOn = false, reducer = toggleReducer} = {}) {
// TODO
}
const {on, getTogglerProps, getResetterProps} = useToggle({
reducer: toggleStateReducer,
})
2.3 final version
import * as React from "react";
import { Switch } from "./switch";
const callAll = (...fns) => (...args) => fns.forEach((fn) => fn?.(...args));
const actionTypes = {
TOGGLE: "TOGGLE",
RESET: "RESET"
};
function toggleReducer(state, { type, initialState }) {
switch (type) {
case actionTypes.TOGGLE: {
return { on: !state.on };
}
case actionTypes.RESET: {
return initialState;
}
default: {
throw new Error(`Unsupported type: ${type}`);
}
}
}
function useToggle({ initialOn = false, reducer = toggleReducer } = {}) {
const { current: initialState } = React.useRef({ on: initialOn });
const [state, dispatch] = React.useReducer(reducer, initialState);
const { on } = state;
const toggle = () => dispatch({ type: actionTypes.TOGGLE });
const reset = () => dispatch({ type: actionTypes.RESET, initialState });
function getTogglerProps({ onClick, ...props } = {}) {
return {
"aria-pressed": on,
onClick: callAll(onClick, toggle),
...props
};
}
function getResetterProps({ onClick, ...props } = {}) {
return {
onClick: callAll(onClick, reset),
...props
};
}
return {
on,
reset,
toggle,
getTogglerProps,
getResetterProps
};
}
function App() {
const [timesClicked, setTimesClicked] = React.useState(0);
const clickedTooMuch = timesClicked >= 4;
function toggleStateReducer(state, action) {
if (action.type === actionTypes.TOGGLE && timesClicked >= 4) {
return { on: state.on };
}
return toggleReducer(state, action);
}
const { on, getTogglerProps, getResetterProps } = useToggle({
reducer: toggleStateReducer
});
return (
<div>
<Switch
{...getTogglerProps({
disabled: clickedTooMuch,
on: on,
onClick: () => setTimesClicked((count) => count + 1)
})}
/>
{clickedTooMuch ? (
<div data-testid="notice">
Whoa, you clicked too much!
<br />
</div>
) : timesClicked > 0 ? (
<div data-testid="click-count">Click count: {timesClicked}</div>
) : null}
<button {...getResetterProps({ onClick: () => setTimesClicked(0) })}>
Reset
</button>
</div>
);
}
export default App;
总结
这里讲到的方法重写或者控制反转的设计模式,更多就是提供用户更多的configuration,让组件或者API有更好的扩展性