乐趣区

关于前端:简化React-Hook的5种方法

在编写自定义 Hook 时,很容易产生过于简单的解决方案。这有时会导致不稳固的行为,创立无用的从新渲染,或者只是使其更难保护。思考到这一点,我想分享 5 种我发现的帮忙简化定制 Hook 的办法。

缩小 useState 数量

当你应用 hook 进行开发时,很容易应用过多的 useState 调用,或者将所有的状态缩减为繁多的、过于简单的 useState。进步 hook 的可读性的最好办法之一就是优先思考你的 useState 调用。我喜爱在我写的钩子中遵循一些对于状态实现的规定。

优先思考易读性

我更喜爱将状态读取为对象,而不是应用多个具备简略值的 useState 命令。应用更少的 useState 命令也会让你的钩子的返回更容易,并且在组件中的实现更间接。尽管这是我的偏好,但代码是一个十分个人化的货色,也是十分有表现力的。我写代码时的第一条规定是 优先思考可读性,遵循这个规定会让你的代码更容易保护,迫使你去思考你所写的货色,并让他人更容易遵循你的代码。如果这是你从这个文章中带走的惟一货色,那么我曾经实现了我的工作。

评估状态对象的内容

组件从一开始就没有被完满地布局过,随着组件的增长,你的 useState 中蕴含的属性可能也会变得越来越简单。在整个开发周期中,我强烈建议评估你的 useState 调用的内容,以确定将状态局部分成其余 useState 调用是否有意义。你可能想按性能或类型对状态值进行分组。一般来说,我喜爱把状态数据依照我认为通常会一起更新的属性来分组,或者依照状态属性的性能来分组,比方数据和视图属性。

利用你的 Hook 返回

当我刚开始写自定义 Hook 时,很容易遵循相似于默认的 useState 钩子的返回款式。尽管这并不是好事,但在函数之上应用一个返回数组来返回多个状态变量,会很麻烦。设想一下,除了解决数据抉择的函数外,还能够返回 2 个不同的状态变量(1 个是数据状态,1 个是视图状态)的钩子,用数组格调的返回形式编写,它可能看起来像这样。

function useBasicHook() {const [dataState, setDataState] = useState({serverData: {},
    selections: {}});
  const [viewState, setViewState] = useState({
    menuExpanded: false,
    submitFormData: {}})
  
  const toggleMenuExpand = () => {
    setViewState({
      menuExpanded: !viewState.menuExpanded,
      submitFormData: viewState.submitFormData
    })
  }
  
  return [dataState, viewState, toggleMenuExpande];
}

function BasicComponent(){const [dataState, viewState, toggleMenuExpand] = useBasicHook();
  
  return <div>
    </div>
}

看看这个 hook,很容易看出,如果在返回中增加额定的函数或变量,hook 的实现会很快失控。如果你不小心毁坏了数组的程序,或者用不正确的名称,会造成额定的凌乱和可能的谬误。咱们能够通过更新 hook 返回一个对象来避免这种状况的产生,就像这样。

function useBasicHook() {const [dataState, setDataState] = useState({serverData: {},
    selections: {}});
  const [viewState, setViewState] = useState({
    menuExpanded: false,
    submitFormData: {}})
  
  const toggleMenuExpand = () => {
    setViewState({
      menuExpanded: !viewState.menuExpanded,
      submitFormData: viewState.submitFormData
    })
  }
  
  return {
    dataState: dataState,
    viewState: viewState,
    toggleMenuExpand: toggleMenuExpand
  };
}

function BasicComponent(){const state = useBasicHook();
  // or
  // const {dataState, viewState, toggleMenuExpand} = useBasicHook();
  
  return <div>
    </div>
}

将返回值转换为对象还有其余益处,包含:

  • 如果 hook 在多个组件之间共享或作为库共享,则在更新后进步 hook 版本的兼容性;
  • 在应用 Hook 在组件顶部提供雷同级别的 Hook API 时,依然能够解构对象。

还有一件很酷的事件,你能够用你的钩子返回,就是在你的状态中创立基于组件工厂函数的小状态。这提供了一种很好的形式,能够将组件构建器共享给实现钩子的组件,而无需将状态公开给该组件。

应用合并钩子简化 setState 调用

在 React 中应用类而不是基于函数的组件进行开发,当波及到状态治理时,的确有一些开箱即用的劣势,对我来说,最次要的是旧状态与新状态的合并。React Docs for State 提供了 React.Component 中内置的状态合并性能的良好示例。尽管该性能没有间接内置到钩子中,但咱们能够通过一个简略的自定义钩子来复制这种行为,它能够替换咱们的 useState 调用,给咱们同样的行为。

function useMergeState(initialState) {const [state, setState] = useState(initialState);
  // 应用 useRef 来改良异步调用 setState 时的性能。const stateRef = useRef(state);

  function setRefState(newState) {
      stateRef.current = newState;
      return setState(newState);
  }

  function mergeState(newState) {
    var finalState = newState;
    /**
     * 判断状态数据类型是否匹配,如果匹配,则持续合并,* 如果不匹配,则抛出一个控制台正告,用新的状态笼罩。*/
    if (typeof stateRef.current !== typeof newState) {
      console.warn("useMergeState warning: 状态数据类型不匹配,用新的状态笼罩状态。");
      finalState = newState;
    } else {
      /**
       * 在此解决合并
       */
      if (typeof stateRef.current == "object" && !Array.isArray(stateRef.current)) {
        // 现有状态是一个对象,持续尝试合并
        if (typeof newState == "object" && !Array.isArray(newState)) {finalState = { ...stateRef.current, ...newState};
        }
      }
    }

    return setRefState(finalState);
  }

  return [stateRef.current, mergeState];
}

思考拆分 Hook

无论组件的复杂程度如何,我总是倡议应用自定义钩子;然而,在构建自定义钩子时,将一个过于简单的钩子宰割成多个较简略的钩子是十分有用的。在我的我的项目中,我喜爱依据性能来拆分钩子逻辑,比如说,把一个钩子拆成逻辑上的状态子集,比方数据 /Web API 交互的钩子和显示状态的独自的钩子,可能会有益处。回忆一下钩子返回局部的例子钩子,这样拆开来可能会有帮忙。

function useDataHook() {const [dataState, setDataState] = useState({serverData: {},
    selections: {}});
  
  return dataState;
}

function useDisplayHook() {const [viewState, setViewState] = useState({
    menuExpanded: false,
    submitFormData: {}})
  
  const toggleMenuExpand = () => {
    setViewState({
      menuExpanded: !viewState.menuExpanded,
      submitFormData: viewState.submitFormData
    })
  }
  
  return {
    viewState: viewState,
    toggleMenuExpand: toggleMenuExpand
}

function BasicComponent(){const data = useDataHook();
  const display = useDisplayHook();
  
  return <div>
    </div>
}

拆分后的示例挂钩

评估 useEffect 调用,以避免不必要的从新渲染

useEffect钩子十分有用,然而如果使用不当,可能会导致适度渲染。查看自定义钩子时,值得评估你的 useEffect 调用。我喜爱恪守以下教训法令:

  • 如果一个 useEffect 在同一个钩子作用域中监听状态变量,那么这个成果不应该更新状态自身。
  • 如果你有多个 useEffect 语句在侦听同一组变量,请思考将它们组合在一起。
  • 只管联合应用 useEffects 有助于缩小从新渲染次数,但首先要优先思考代码的可读性。

起源:https://levelup.gitconnected.com,作者:Ben Simons,翻译:公众号《前端全栈开发者》

退出移动版