译不要再问我React-Hooks能否取代Redux了

38次阅读

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

原文地址:Stop Asking if React Hooks Replace Redux

许多同事一直问我一些类似的问题:

“如果我们在项目中使用 hooks,我们是否还需要 Redux?”

“React Hooks 会不会使 Redux 太过时了?我能不能用 Hooks 来做所有 Redux 能做的事呢?”

在 Google 中搜索会发现,大家经常问这些问题。

“React Hooks 是否会取代 Redux?”,最简单的回答是“不一定”。

更细致但礼貌的答案是“嗯,那取决于你正在做的项目类型”。

我更倾向于告诉大家的答案是“我不确定你是否知道你在说什么”。有几个原因可以说明,为什么“React Hooks 是否会取代 Redux”是一个本质上有缺陷的问题。首先:

Redux 一直是非强制性的

通过 Dan Abramov(Redux 的创造者之一)的一篇文章【You Might Not Need Redux】可以看出,如果你不需要使用它,则无需替换任何东西。

Redux 是一个 JavaScript 库,并且如果你用的是 React(另一个 JavaScript 库),那么为了使用 Redux,你还需要在应用中加载一个 React-Redux 的 JavaScript 库。在项目中使用依赖库会增加打包体积,这会增加你应用的加载时间。基于这个原因,你不应该使用一些库,像 jQuery、Redux、MobX(另一个状态管理库),甚至是 React,除非你有明确的理由要使用它们。

当大家问到“是否 hooks 会替代 Redux”,他们似乎经常觉得,他们的 React 应用需要使用其中一种。事实并非如此,如果你正在写的应用没有很多状态需要被储存,或者你的组件结构很简单,可以避免过度的 prop 传递,以及基于 React 本身提供的特性,你的状态已经足够可控了,不管有没有 hooks,这些情况使用状态管理就没有多大意义了。

即使你确实有许多的状态,或者有像老树根一样扭曲分叉的 React 组件结构,你仍然不需要状态管理库。Prop 传递可能很麻烦,但是 React 给了你许多状态管理选项,并且 hooks 绝对可以帮你很好地组织状态。Redux 是一个轻量级的库,但是它的设置很复杂,增加了打包体积,并且很多地方需要权衡。有很多原因可以说明,为什么你应该选择不在项目中使用它,并且这些原因很有说服力。

你并不总是需要 Redux,这也是在说,你依然有许多理由去使用它的。如果你的项目在一开始就使用了 Redux,那么它可能是一个很好的理由,无论它是否做了这些:组织(应用状态的可预测性、单一的数据流,在复杂的应用中很有用)、中间件、Redux 的强有力的开发工具和调试能力。如果你有使用 Redux 的理由,它不会因为 React Hooks 变得无效。如果你之前需要 Redux,那么你现在仍然需要。这是因为:

React Hooks 和 Redux 并没有试图解决同样的问题

Redux 是一个状态管理库,Hooks 是 React 最近更新的部分特性,让你的函数组件可以做类组件能做的事情。

所以不使用类组件来写 React 应用突然会让状态管理库变得过时了呢?

当然不会!

通过文档可以看出,React Hooks 被开发出来主要是这三个理由:

  • 难以复用类组件之间的逻辑
  • 生命周期中经常包含一些莫名其妙的不相关逻辑
  • 类组件难以被机器和人理解

注意,没有一条理由的动机直接表明要做一些与状态管理相关的事情。

话说如此,React Hooks 确实提供了一些选择去管理应用的状态。尤其是 useStateuseReduceruseContext方法,提供来新的方式去维护你的状态,这被证明比先前 React 提供的选项更好、更有条理。

但是这些 hooks 并不是什么新东西或神奇的东西,并且它们也没有使状态管理过时,因为事实是:

React Hooks 并没有让你的应用可以做一些以前做不到的事情

那就对了,你现在可以写函数组件来做一些以前只能用类组件来做的事情,但是这些函数组件并不能做一些类组件做不到的事情,除了可以更好地组织和复用代码的能力。它们不一定让你的应用更好,而是让开发者的体验更好。

useStateuseReducer 只是管理组件状态的方法,并且它们的工作原理同类组件的 this.statethis.setState是一样的,你仍然需要传递你的 props。

useContext是大家认为在 Redux 板上钉钉的特性,因为它可以让你在组件之间共享应用的状态,而不需要通过 prop 传递,但是它也没有真正的做任何新的事情。context API 现在是 React 的一部分,useContext仅仅是让你不用 <Consumer> 包裹也可以使用 context。并且有一些开发这用 context 来管理整个应用的状态,这不是设计 context 的目的。通过文档可以看出:

Context is designed to share data that can be considered“global”for a tree of React components, such as the current authenticated user, theme, or preferred language.

Context 是为了共享数据而被设计出来的,可以认为是 React 组件树的“全局”,比如当前已授权的用户、主题或者首选的语言。

换句话说,就是那些预计不会频繁更新的东西。

文档中也建议有节制地使用 context,因为“它会使得组件难以复用”。他们也提醒开发者,如果开发者不小心,context 很容易触发不必要的重复渲染。

我见过项目成功地使用 React Context 来管理应用状态,这是有可能的,也不失为一种选择。但是状态管理并不完全是 context 被设计出来去做的事情,而且 Redux 和其他状态管理库被设计出来就是为了处理这种特定的目的。

此外,React Hooks 也绝不意味着 Redux 的消亡,因为如果你看一眼 React-Redux 最近更新的文档,你会明白:

React-Redux 也有自己的 hooks

没错,React Hooks 正在帮助 React-Redux 恢复活力并移除来它的一些痛点,与“替代”的说法相差甚远。

我在另一篇文章中对 React-Redux 进行了深入研究,这里要说的重点。在 hooks 之前,你必须定义 mapStateToPropsmapDispatchToProps两个函数,并且用 connect 包裹你的组件来创建一个高阶组件,它会传递 dispatch 方法和部分 Redux 贮存的状态,这些状态是你在 mapping 函数中指定作为 props 传递到组件中的。

让我们来看一个非常简单的计数器应用的例子(太简单甚至都不需要 Redux,但是这里主要是为了展示一些信息)。假设我们已经定义了 Redux store 和 incrementdecrement 两个 action creator(完整的源码在这里)。

import React from 'react';
import {connect} from 'react-redux';
import * as actions from '../actions/actions';

class App extends React.Component {constructor(props) {super(props);
  }

  render() {const {count, increment, decrement} = this.props;

    return (
      <div>
        <h1>The count is {count}</h1>
        <button onClick={() => increment(count)}>+</button>
        <button onClick={() => decrement(count)}>-</button>
      </div>
    );
  }
}

const mapStateToProps = store => ({count: store.count});

const mapDispatchToProps = dispatch => ({increment: count => dispatch(actions.increment(count)),
  decrement: count => dispatch(actions.decrement(count))
});

export default connect(mapStateToProps, mapDispatchToProps)(App);

太令人烦恼了!如果我们不必包裹组件到高阶组件中,就可以让组件取到 Redux store 的值,这样不是更友好吗?是的,这就是 hooks 出现的原因。Hooks 就是为了复用代码和消除由于高阶组件产生的“嵌套地狱”。下面是一个相同的组件,使用 React-Redux hooks 转换成函数组件。

import React from 'react';
import * as actions from '../actions/actions';
import {useSelector, useDispatch} from 'react-redux';

const App = () => {const dispatch = useDispatch();
  const count = useSelector(store => store.count);

  return (
    <div>
      <h1>The count is {count}</h1>
      <button onClick={() => dispatch(actions.increment(count))}>+</button>
      <button onClick={() => dispatch(actions.decrement(count))}>-</button>
    </div>
  );
}

export default App;

是不是很漂亮?简而言之,useSelector让你可以保存部分 Redux store 的值到你的组件。useDispatch更简单,它仅仅为你提供了一个 dispatch 函数,你可以用它来发送状态更新到 Redux store。最棒的是,你不再需要写这些丑陋的 mapping 函数和用 connect 函数来包裹组件。现在,一切都很好地包含在你的组件中,它更简洁,因此更容易阅读,并且更有条理。重点是:

没有必要比较 React Hooks 和 Redux 孰优孰劣

毫无疑问,这两项技术可以很好地互补。React Hooks 不会替代 Redux,它们仅仅为你提供来新的、更好的方式去组织你的 React 应用。如果你最终决定使用 Redux 来管理状态,可以让你编写更好的连接组件。

所以,请不要再问“React Hooks 是否会取代 Redux?”。

相反,开始问自己“我正在制作什么样的应用?我需要什么样的状态管理?Redux 可以用吗,还是有些过度使用呢?hooks 可以用吗,还是应该用类组件?如果我决定使用 Redux 和 React Hooks(或者 MobX 和 React Hooks,或者 Redux 和 jQuery,不用 React——这些都是有效的选择,取决于你正在做的事情),那么我怎样可以使这些技术互补并且和谐共处呢?”。

正文完
 0