rccoder/blog

听说你需要这样了解 Redux(二)

rccoder opened this issue · 1 comments

1. 前言

很久很久之前,写过一篇介绍 Redux 相关概念的文章 听说你需要这样了解 Redux(一),随后因为实习的事情拖延了后续(根本原因是懒)。

在实习中也更加深刻的认识了 Redux 相关的内容。除此之外,翻译 Redux 中文文档 redux-in-chinese 也让我静心下来仔细品读了设计理念。

这篇文章中我将介绍一个如何把 React 和 Redux 这两个大杀器结合起来,同时也会介绍一些 state、reducer 设计相关的东西。

2. 正文

2.1 连接神器 react-redux

react 和 redux 可以说是没有任何关系,redux 主要提供一个全局的 store,然后 react 从 store 拿去数据,store 发生变化,react 重新进行渲染。

在远古时代我们就已经知道频繁操作 DOM 是很耗费性能的,react 在数据发生变化的时候就会进行 render,虽然我们可以用 PureRenderMixin 或者在 shouldComponentUpdate 做一些处理,但对于一个页面的数据来说,这好像也是非常麻烦的。

react-redux 的出现就解决了这个问题,并且保证了整个应用程序的灵活性与可维护性。用官方的话说就是:

Performant and flexible

react-redux 提供了两个耳熟能详的 API: <Provider store>connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

在上篇文章中已经简单介绍过这两个 API 的基础概念:

Provider 是在原有的 APP 上面包一层,然后接收 store 作为 props,然后给 connect 用

connect 接收 store 提供的 state 和 action,返回给我们的 react 组件;还有一个很重要的一点是在 connect 这层 react-redux 做了优化,保证给组件只传和他相关的 state,这也就保证了性能上的优势

2.2 搭个架子

//index.jsx
import { ReactDOM } from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.querySelector('#app')
);

// App.jsx
import { React } from 'react';
import { connect } from 'react-redux';

class App extends Component {

  render() {
    <div>Test<div>
  }
}

export default connect(state => state, actions)(App);

这里 connect 只传递了两个参数 state => stateactions

state => state 就是 mapStateToProps 的函数,他会监听 store 的变化,只要 store 变化,这个函数就会被调用。mapStateToProps 返回一个对象,这个对象会与组件的 props 合并。mapStateToProps 接收两个参数 stateownProps,通常情况下我们只需要第一个参数,如果有第二个参数 ownProps,其也将传给组件的 props,并且当组件的 props 发生变化时也会调用 mapStateToProps。这个例子中进去与返回的都是 state,这在实际开发中是不科学的,为了保证我们应用的性能,我们应该只返回需要的 state 切片。

action 就是 mapDispatchToProps,他可以是个函数,也可以是个对象。如果是个函数,这个函数将接收一个 dispatch 函数,然后由我们决定返回怎样的一个对象,这个对象会通过 dispatch 函数与 action creator 绑定起来;如果是个对象,则这个对象中的函数都会被当做 action creator,而且这个对象会与 store 绑定在一起,其中所定义的方法名将作为属性名,合并到组件的 props 中。一般情况下会省略这个参数。

mergeProps 用于自定义 props 合并,默认返回 Object.assign({}, ownProps, stateProps, dispatchProps)

options 是一个优化开关,这里不做太多解释(我还没用过啊!)。

关于 connect 详细的说明,还是建议阅读官方文档:https://github.com/reactjs/react-redux/blob/master/docs/api.md

这里有个小聪明可以玩,如果你已经用上了 babel 全家桶并且是一个激进的人,可以用装饰器优雅的写 connect:

@connect(state => state, actions)

export default class App extends Component {
  render() {
    <div>Test<div>
  }
}

2.3 store 绑定

从用 React 的第一天起,“单向数据流” 的概念就已经扎进了心口。在上面的例子中,我们可以看到一个简单清晰的流向:

从 store -> connect 层做处理 -> 组件 props

那在实际的交互中,又是如何的往 store 中塞入数据的呢?这个就和 Redux 连接起来了。

import {createStore, applyMiddleware, compose} from "redux";
import thunkMiddleware from "redux-thunk";

const rootReducer = (state, action) => {
  let result = state;
  switch(action.type) {
    case "TYPE_A":
      // 省略
      break;
    default:
      break;
  }
  return result;
}

const initialState = {};
const middleware = [thunkMiddleware];

const store = createStore(
  rootReducer,
  initialState,
  compose(
    applyMiddleware(...middleware),
    window.devToolsExtension ? window.devToolsExtension() : f => f
  )
)

和上章节中一样,用 Redux 提供的 createStore 创建 Redux store,然后把 store 在 Redux 提供的 Provider 中传递进去。

同样,通过 store.dispatch({type: 'xx', text: 'xx'}) 就能融入整个 Redux 中。每当 store 发生变化,react-redux 就会处理 subscribegetState,传给相应的组件,同时也保持了比较高的性能。

2.4 state、reducer 设计

参考我翻译的 redux 文档:State 范式化管理范式化数据

里面有详细的说明,并且有很不错的例子。更应该的,你应该详细阅读一下所有文档,尤其是 组织 Reducer 这块。

参考资料

系列文章

EOF