React中的设计模式 - 方法重写

前言

最近开始学习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有更好的扩展性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值