你也许并不需要 redux

redux 给包括我在内的绝大多数人带来的一个痛点是牵一发而动全身,以及文件累赘。比如我只需要实现一个请求数据的功能时,理论上只需要一行 axios 代码实现,但是我同时需要改动四个文件:reducer、action、selector、component;并且每一个请求的 reducer 数据结构几乎是相同的(都由 isLoading、data、error)组成,每个请求的 action 类型几乎也是相同的(无外乎requestBegin、requestSuccess,requestFailure),然而我还是要往复的重复这些工作。

不仅如此 Redux 还要求:

  • 把程序的状态用纯粹的对象和数组表达
  • 把状态的变化用纯粹的对象表达
  • 把业务处理逻辑放在 pure function 中

你不如这里理解:以上的这些痛点,或者说是限制,都是使用 redux 的代价,而在这些代价的背后,redux 提供了开发 web app 的便利,例如实现撤销功能或者调试时重现 bug。

或许你长久以来都有一个疑问:我应该把所有的数据或者说状态都放到 redux 的 store 里吗?还是应该放在组件的 state 里?

假设你真的把所有的状态都存放在 store 里,那么状态会变得庞大并且混乱(甚至包含界面UI),随之 reducer 和 action 的数量也水涨船高。其实并不是所有的程序都需要操作回放,也不是所有的操作都需要被回放,最好还是根据回放的实际需求再组织状态的存储位置。

这个问题没有标准答案(这个不是我说的,是 redux 作者自己说的 ),你应该为你的程序找到最佳的使用方式,或者你维护起来最舒服的方式。

当然,在衡量是否应该把数据放在 redux 中时,有一些标准可以拿来参考的:

  • 程序中的其他组件也需要这部分数据吗?
  • 你需要根据原始数据来衍生出新的数据吗?
  • 同一份数据是否需要用来驱动多个组件?
  • 是否有需求需要还原程序的状态(比如在 debug 的时候)?
  • 你需要缓存数据吗?

如果以上你的答案都是 Yes ,那么你应该考虑把这部分数据放入 redux 里公共的 store 中了。否则实际代码中的很多功能仅仅在组件内部就可以完成

如果你同意这样的观点,并且正在考虑将 store 的状态迁往组件中,最简单的办法是丝毫不动的移植过来并且作为 state 即可

同时对于一些公用的数据请求方法,也可以以 High Order Component 的方法抽象出来,之后便方便注入到多个组件中,这个我们在 High Order Component 章节中再具体举例

接下来举两个实际的例子

简单的异步问题不需要使用 redux

在填写表单的时候,我们常常需要检测用户名是否可用。想象一下如果使用 redux 解决这个问题的话,我们需要做以下工作:

// 创建检测初始状态
{
  loading: true,
  checkResponse: '',
  error: false,
}
// 创建对应的检测 action
import {createAction} from 'redux-actions'
const checkNameAvailableBegin  = createAction(types.CHECK_NAME_AVAILABLE_BEGIN)
const checkNameAvailableSuccess  = createAction(types.CHECK_NAME_AVAILABLE_SUCCESS)
const checkNameAvailableError  = createAction(types.CHECK_NAME_AVAILABLE_ERROR)
// 创建对应的 reducer
// TODO
// 创建对应的 Saga / Thunk
// TODO
// 在组件中做对应的修改
// TODO

但事实上也完全可以在对应的组件内用普通的 get 方法解决:

nameInputChangeHandler = (nameValue) => {
    this.setState({
        loading: true
    })
    axios.get(`/api/check?name=${nameValue}`).then(response => { 
      this.setState({
        loading: false,
        available: response.data,
      })
    }).catch(() => this.setState({ loading: false, available: false }))
}

react-refetch

react-refetch 是我非常喜欢的一个非 redux 的解决方案,在这里推荐给大家

Heroku 团队也同样热爱 redux ,但是发现一个问题,根据 redux 数据流,他们需要将数据从远程加载到 store 中,又把数据再一次从 store 中选择出来,最后渲染到程序中。就像上面说的,他们认为这样的模式更适用于数据需要在组件中需要共享或者需要缓存到浏览器中的情况。当时在当前场景中,仅仅是需要将数据从远程加载然后渲染出来,这就显得大材小用了。

另一方面,他们发现程序的状态完全可以由 url 来表达。所以他们把状态属性整合进请求中,再通过请求从后端抓取数据,注入到组件中并予以渲染。

以上这套机制就被抽象为类库 react-refetch​

react-refetch 与 redux 相似,提供名为 connect 的 HOC 函数向组件中注入属性,为了不与 redux 的 connect 发生冲突,我们可以命名为其他名称:

import {connect as refetchConnect} from 'react-refetch' 假设你现在有一个 Profile 组件,用于展示用户信息。但首先你需要请求用户信息,并且注入到 Profile 组件中。这个操作借助 react-refetch 如下:

export default refetchConnect((props) => ({
  userFetch: `/api/users/${props.params.userId}`
}))(Profile)

那么接下来如何获取到请求的信息以及发出的请求的状态呢?在组件中通过访问 userFetch 属性即可:

render() {
  const { userFetch } = this.props 
​
  if (userFetch.pending) {
    return <LoadingAnimation/>
  } else if (userFetch.rejected) {
    return <Error error={userFetch.reason}/>
  } else if (userFetch.fulfilled) {
    return <User user={userFetch.value}/>
  }
}

这是最基本的功能演示,事实上功能还包括链式请求、周期性刷新数据、懒加载等等+

results matching ""

    No results matching ""