关于javascript:译-React-Native-教程-13-React-Hook状态管理-无需Redux和Context

39次阅读

共计 4632 个字符,预计需要花费 12 分钟才能阅读完成。

之前的教程 12 篇

明天,咱们将对它进行摸索,并开发一个自定义的 Hook 来治理全局状态 – 这是一个比 Redux 更容易应用的办法,而且比 Context API 更有性能。

钩子的基础知识

如果你曾经相熟 React Hooks,你能够跳过这部分。

useState()

在 Hooks 之前,性能组件没有状态。当初,有了 “useState()”,咱们能够做到这一点。

它的工作原理是返回一个数组。上述数组的第一项是一个变量,它提供了对状态值的拜访。第二项是一个函数,它更新组件的状态以反映 DOM 上的新值。

useEffect()

类组件应用生命周期办法治理副作用,如 componentDidMount()useEffect() 函数能够让你在函数组件中执行副作用。

默认状况下,成果会在每次实现渲染后运行。然而,你能够抉择只在某些值发生变化时才启动它,传递一个变量数组作为第二个可选参数。

为了失去和 componentDidMount() 一样的后果,咱们能够发送一个空数组。晓得空数组永远不会扭转,所以成果只会运行一次。

咱们能够看到,Hooks 状态的工作原理和类组件状态齐全一样。组件的每个实例都有本人的状态。

为了工作在组件之间共享状态的解决方案,咱们将创立一个自定义的 Hook。

咱们的想法是创立一个监听器数组和惟一一个状态对象。每当一个组件扭转状态时,所有被订阅的组件都会被触发 setState() 函数并失去更新。

咱们能够通过在咱们的自定义 Hook 外面调用 useState() 来实现。然而,咱们不返回 setState() 函数,而是将其增加到一个监听器数组中,并返回一个更新状态对象和运行所有监听器函数的函数。

等等,这不是应该让我的生存更轻松吗?

是的,我创立了一个 NPM 包,封装了所有这些逻辑。

use-global-hook

在不到 1kb 的范畴内应用钩子对 react 进行简略的状态治理。

www.npmjs.com

你不须要在每个我的项目中重写这个自定义钩子。如果你只是想跳过并应用最终的解决方案,你能够通过运行以下命令轻松地将其增加到你的我的项目中。

npm install -s use-global-hook。

你能够通过软件包文档中的例子来学习如何应用它。然而,从当初开始,咱们将重点介绍它的工作原理。

第一版


import {useState, useEffect} from 'react';

let listeners = [];
let state = {counter: 0};

const setState = (newState) => {state = { ...state, ...newState};
  listeners.forEach((listener) => {listener(state);
  });
};

const useCustom = () => {const newListener = useState()[1];
  useEffect(() => {listeners.push(newListener);
  }, []);
  return [state, setState];
};

export default useCustom;

第一个版本曾经能够共享状态。你能够在你的应用程序中增加任意多的 Counter 组件,并且它将领有雷同的全局状态。

import React from 'react';
import useCustom from './customHook';

const Counter = () => {const [globalState, setGlobalState] = useCustom();

  const add1Global = () => {
    const newCounterValue = globalState.counter + 1;
    setGlobalState({counter: newCounterValue});
  };

  return (
    <div>
      <p>
        counter:
        {globalState.counter}
      </p>
      <button type="button" onClick={add1Global}>
        +1 to global
      </button>
    </div>
  );
};

export default Counter;

但咱们能够做得更好。在第一个版本中我不喜爱的中央。

  • 我想在组件卸载时从数组中移除监听器。
  • 我想让它更通用,这样咱们就能够在其余我的项目中应用。
  • 我想通过参数设置一个 “initialState”。
  • 我想应用更多面向函数的编程。

在组件卸载前调用一个函数。

咱们理解到,用空数组调用 “useEffect(function,[])”,与 “componentDidMount() “ 有雷同的用处。然而,如果在第一个参数中应用的函数返回另一个函数,那么这个第二个函数将在组件被卸载之前被启动。和 componentWillUnmount() 齐全一样。

这是将组件从监听器数组中移除的最佳地位。

第二版

const useCustom = () => {const newListener = useState()[1];
  useEffect(() => {
    // Called just after component mount
    listeners.push(newListener);
    return () => {
      // Called just before the component unmount
      listeners = listeners.filter(listener => listener !== newListener);
    };
  }, []);
  return [state, setState];
};

除了这最初的批改,咱们还将。

  • 将 React 设置为参数,不再导入它。
  • 不导出 customHook,而是导出一个函数,依据 initialState 参数返回一个新的 customHook。
  • 创立一个 “store “ 对象,其中蕴含 “state “ 值和 “setState() “ 函数。
  • 替换 setState()useCustom()中的箭头函数为惯例函数,这样咱们就能够有一个上下文来将 store 绑定到this
function setState(newState) {this.state = { ...this.state, ...newState};
  this.listeners.forEach((listener) => {listener(this.state);
  });
}

function useCustom(React) {const newListener = React.useState()[1];
  React.useEffect(() => {
    // Called just after component mount
    this.listeners.push(newListener);
    return () => {
      // Called just before the component unmount
      this.listeners = this.listeners.filter(listener => listener !== newListener);
    };
  }, []);
  return [this.state, this.setState];
}

const useGlobalHook = (React, initialState) => {const store = { state: initialState, listeners: [] };
  store.setState = setState.bind(store);
  return useCustom.bind(store, React);
};

export default useGlobalHook;

因为咱们当初有了一个比拟通用的 Hook,所以咱们要在 store 文件中设置它。

import React from 'react';
import useGlobalHook from './useGlobalHook';

const initialState = {counter: 0};

const useGlobal = useGlobalHook(React, initialState);

export default useGlobal;

将口头与组件离开

如果你已经应用过简单的状态治理库,你就会晓得间接从组件中操作全局状态不是最好的主见。

最好的办法是通过创立操纵状态的动作来拆散业务逻辑。出于这个起因,我心愿咱们解决方案的最初一个版本不给组件拜访 setState() 函数,而是给一组动作。

为了解决这个问题,咱们的 useGlobalHook(React, initialState, actions) 函数将接管一个 action 对象作为第三个参数。对于这一点,我想补充一些货色。

  • Actions 将能够拜访 store 对象。因而,动作能够通过 store.state 读取状态,通过 store.setState() 写入状态,甚至应用 state.actions 调用其余动作。
  • 对于组织,动作对象能够蕴含其余动作的子对象。因而,你可能有一个 actions.addToCounter(a amount) 或者一个蕴含所有计数器动作的子对象,用 actions.counter.add(a amount) 调用。

最终版本

以下文件是 NPM 包 use-global-hook 中的理论文件。

function setState(newState) {this.state = { ...this.state, ...newState};
  this.listeners.forEach((listener) => {listener(this.state);
  });
}

function useCustom(React) {const newListener = React.useState()[1];
  React.useEffect(() => {this.listeners.push(newListener);
    return () => {this.listeners = this.listeners.filter(listener => listener !== newListener);
    };
  }, []);
  return [this.state, this.actions];
}

function associateActions(store, actions) {const associatedActions = {};
  Object.keys(actions).forEach((key) => {if (typeof actions[key] === 'function') {associatedActions[key] = actions[key].bind(null, store);
    }
    if (typeof actions[key] === 'object') {associatedActions[key] = associateActions(store, actions[key]);
    }
  });
  return associatedActions;
}

const useGlobalHook = (React, initialState, actions) => {const store = { state: initialState, listeners: [] };
  store.setState = setState.bind(store);
  store.actions = associateActions(store, actions);
  return useCustom.bind(store, React);
};

export default useGlobalHook;

正文完
 0