Java编程网

分享 Java Web 开发相关知识

使用Hooks + Context,而不是React + Redux

Ebenezer Don✏️撰写

Redux需要大量的代码,给我们的代码库带来了很多复杂性。充其量,这使其成为React应用程序中状态管理的不完善解决方案。但是,太多的React开发人员默认使用Redux进行状态管理,而没有考虑其他选择。

在本文中,我将介绍用于状态管理的React Context API,并向您展示什么使React Hooks和Context API成为比Redux更好的解决方案。

为什么我们需要状态管理工具

在典型的React中,处理断开的组件之间的数据的方法是通过钻探。例如,由于您不希望组件从顶级组件传递数据到第五级组件,因此组件无法访问全局状态,因此必须将数据作为道具传递给树的每个级别直到找到所需的组件。

这导致编写大量额外的代码,并为组件提供永远不会使用的属性,这也会影响其体系结构设计。为了解决这个问题,我们需要一种方法来提供一个全局状态,所有组件(无论它们嵌套的深度如何)都可以访问。

通过解决这个问题,用于管理应用程序状态的开源JavaScript库Redux成为了React开发人员的首选解决方案。

《使用Hooks + Context,而不是React + Redux》

Redux如何工作

终极版文档描述它为JavaScript应用程序可预测的状态容器,可以帮助我们,其行为一致,在不同的环境中运行,并且很容易将试写的应用程序。

道具钻探的一个缺点是需要编写大量额外的代码才能访问顶级组件中的数据。使用Redux,由于为我们的应用程序设置全局状态所需的所有额外代码都带来了许多复杂性,因此甚至感觉到了这种劣势。Redux需要三个主要的构建部分来起作用:动作,化简和存储。

动作

这些是用于将数据发送到Redux存储的对象。它们通常具有两个属性:用于描述操作作用的类型属性和包含应在应用程序状态下更改的信息的有效负载属性。

// action.js
const reduxAction = payload => {
  return {
    type: 'action description',
    payload
  }
};

export default reduxAction;

type通常为全部大写,以其字分离用下划线。例如,SIGNUP_USERDELETE_USER_DATA

减速器

这些是实现动作行为的纯函数。他们采用当前应用程序状态,执行操作,然后返回新状态:

const reducer = (state, action) => {
  const { type, payload } = action;
  switch(type){
    case "action type":
      return {
        ["action description"]: payload
      };
    default:
      return state;
  }
};

export default reducer;

商店

存储是应用程序状态所在的位置。Redux应用程序中只有一个商店:

import { createStore } from 'redux'

const store = createStore(componentName);

由于我们的应用程序只能有一个Redux存储,因此为了为我们的所有组件创建单个root reducer,我们将需要combineReducersRedux中的方法。

通过如此漫长的过程以及设置Redux所需的大量代码,请想象一下,当我们有多个组件要使用时,我们的代码库将是什么样。尽管Redux解决了我们的状态管理问题,但使用起来确实很耗时,学习曲线很困难,并且给我们的应用程序带来了全新的复杂性。

幸运的是,React Context API解决了这个问题。当与React Hooks结合使用时,我们拥有一种状态管理解决方案,该解决方案的建立时间更少,学习曲线也很容易,并且所需的代码最少。

React Context API

新的Context API随React 16.3一起提供。这是React文档中解释上下文的方式:

上下文提供了一种通过组件树传递数据的方法,而不必在每个级别手动传递道具。

React上下文API是React在未直接连接的多个组件中管理状态的方式。

为了创建一个上下文,我们将使用createContextReact 的方法,该方法接受一个参数作为其默认值:

import React from 'react';

const newContext = React.createContext({ color: 'black' });

createContext方法返回带有Provider和的对象Consumer

const { Provider, Consumer } = newContext;

Provider组件是什么使提供给所有子组件的状态,无论多么深刻嵌套他们是在组件层次结构中。所述Provider组件接收一个value支柱。这是我们传递当前值的地方:

<Provider value={color: 'blue'}>
  {children}
</Provider>

Consumer,顾名思义,消耗来自数据Provider,而无需任何道具钻:

<Consumer>
  {value => <span>{value}</span>}}
</Consumer>

如果没有Hooks,与Redux相比,Context API看起来可能并不多,但是与useReducerHook 结合使用,我们就有了可以最终解决状态管理问题的解决方案。

React中的钩子是什么?

挂钩是一种功能,可以在基本代码中执行自定义代码。在React中,Hooks是特殊功能,可让我们“了解”其核心功能。

React Hooks通过允许我们轻松处理功能组件的状态管理,提供了一种替代编写基于类的组件的方法。

useContext挂钩

如果您注意到了,在解释React Context API时,我们需要将内容包装在一个Consumer组件中,然后以子代形式传递一个函数,以便我们可以访问(或使用)状态。这引入了不必要的组件嵌套,并增加了代码的复杂性。

useContext挂钩使事情要好很多,直接。为了使用它访问我们的状态,我们要做的就是以我们创建context的参数作为参数来调用它:

const newContext = React.createContext({ color: 'black' });

const value = useContext(newContext);

console.log(value); // this will return { color: 'black' }

现在,无需将内容包装在Consumer组件中,我们只需通过value变量访问状态即可。

useReducer挂钩

useReducer钩来与之反应16.7.0。就像reduce()JavaScript中的方法一样,useReducerHook接收两个值作为其参数(在本例中为当前状态和一个动作),然后返回一个新状态:

const [state, dispatch] = useReducer((state, action) => {
  const { type } = action;
  switch(action) {
    case 'action description':
      const newState = // do something with the action
      return newState;
    default:
      throw new Error()
  }
}, []);

在上面的代码块中,我们定义了状态和相应的方法来dispatch处理它。当我们调用该dispatch方法时,useReducer()Hook将根据该type方法在其action参数中接收到的内容执行一项操作:

...
return (
  <button onClick={() =>
    dispatch({ type: 'action type'})}>
  </button>
)

useReducer钩加上上下文API

开设我们的商店

现在我们知道了Context API和useReducerHook分别如何工作,让我们看看将它们结合在一起以为我们的应用程序获得理想的全局状态管理解决方案会发生什么。我们将在store.js文件中创建全局状态:

// store.js
import React, {createContext, useReducer} from 'react';

const initialState = {};
const store = createContext(initialState);
const { Provider } = store;

const StateProvider = ( { children } ) => {
  const [state, dispatch] = useReducer((state, action) => {
    switch(action.type) {
      case 'action description':
        const newState = // do something with the action
        return newState;
      default:
        throw new Error();
    };
  }, initialState);

  return <Provider value={{ state, dispatch }}>{children}</Provider>;
};

export { store, StateProvider }

在我们的store.js文件中,我们使用了前面介绍的createContext()方法React创建了一个新的上下文。请记住,该createContext()方法返回带有ProviderConsumer组件的对象。这次,我们只需要使用Provider组件,然后在useContext需要访问状态时使用Hook。

请注意我们是如何在应用程序中使用useReducerHook的StateProvider。当我们需要操纵状态时,我们将调用该dispatch方法并传入一个具有所需type参数作为参数的对象。

在我们StateProvider,我们回到我们的Provider组件具有value的道具state,并dispatchuseReducer挂钩。

全球访问我们的州

为了全局访问状态,我们需要先将根<App/>组件包装StoreProvider在我们的ReactDOM.render()函数中,然后再将其渲染:

// root index.js file
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { StateProvider } from './store.js';

const app = (
  <StateProvider>
    <App />
  </StateProvider>
);

ReactDOM.render(app, document.getElementById('root'));

现在,context可以从组件树中的任何组件访问我们的商店。为此,我们将从文件中导入useContextHook reactstorefrom ./store.js

// exampleComponent.js
import React, { useContext } from 'react';
import { store } from './store.js';

const ExampleComponent = () => {
  const globalState = useContext(store);
  console.log(globalState); // this will return { color: red }
};

从我们的州添加和删除数据

我们已经看到了如何访问我们的全球状态。为了从状态中添加和删除数据,我们需要上下文中的dispatch方法store。我们只需要调用该dispatch方法并传入一个对象type(其动作描述在StateProvider组件中定义)作为其参数:

// exampleComponent.js
import React, { useContext } from 'react';
import { store } from './store.js';

const ExampleComponent = () => {
  const globalState = useContext(store);
  const { dispatch } = globalState;

  dispatch({ type: 'action description' })
};

结论

在很大程度上,Redux可以在React应用程序中进行状态管理,并且具有一些优势,但是它的冗长性使其难以使用,并且使它在我们的应用程序中运行需要大量的额外代码,这引入了很多不必要的复杂性。

另一方面,使用useContextAPI和React Hooks,无需安装外部库或添加大量文件和文件夹即可使我们的应用正常运行。这使它成为处理React应用程序中全局状态管理的更简单,更直接的方法。


编者注:这篇文章有什么问题吗?您可以在此处找到正确的版本。

插件:LogRocket,用于Web应用程序的DVR

《使用Hooks + Context,而不是React + Redux》

LogRocket是一个前端日志记录工具,可让您像在您自己的浏览器中一样重播问题。LogRocket无需猜测错误发生的原因或向用户询问屏幕截图和日志转储,而是让您重播会话以快速了解出了什么问题。无论框架如何,它都能与任何应用完美配合,并且具有用于记录来自Redux,Vuex和@ ngrx / store的其他上下文的插件。

除了记录Redux动作和状态外,LogRocket还记录控制台日志,JavaScript错误,堆栈跟踪,带有标头+正文,浏览器元数据和自定义日志的网络请求/响应。它还使用DOM来记录页面上的HTML和CSS,甚至可以为最复杂的单页面应用程序重新创建像素完美的视频。

免费试用


使用钩子+上下文,还没有反应过来+终极版首次出现在LogRocket博客

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注