Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

理解 react-redux 之 connect #8

Open
JTangming opened this issue Apr 11, 2017 · 0 comments
Open

理解 react-redux 之 connect #8

JTangming opened this issue Apr 11, 2017 · 0 comments

Comments

@JTangming
Copy link
Owner

JTangming commented Apr 11, 2017

redux 看上去足够简单,但却有非常强的规范约束,作为 react 全家桶中极为重要的组成部分,是 JavaScript 应用程序的可预测状态容器,是一种状态管理的解决方案。

来看其官网上的一个 gist demo:
import { createStore } from 'redux'

function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1
  case 'DECREMENT':
    return state - 1
  default:
    return state
  }
}

let store = createStore(counter)

store.subscribe(() =>
  console.log(store.getState())
)

store.dispatch({ type: 'INCREMENT' }) // 1
store.dispatch({ type: 'INCREMENT' }) // 2
store.dispatch({ type: 'DECREMENT' }) // 1

redux 的使用规范是确保整个应用程序的数据状态存储在单个 store 的对象树中,在 reducer 中更改状态树 state ,唯一方法是 dispatch action,这个 dispatch action 包含 type 及相关变化数据的一个对象。

一个应用往往有复杂的组件嵌套,单纯使用 redux的话,可以在最外层容器组件中初始化 store,通用做法是将 state 上的属性作为 props 层层传递下去,这种实践想想都是令人厌烦的。那接下来我们就引入 react-redux 的 connect。

react-redux 两个主要的 API 是 Provider、connect,我们先从一个简单的例子开始:

// root.js
class Root extends React.Component {
  _store: Store<any>;
  render() {
    return (
      <Provider store={this._store}>
        <App />
      </Provider>
    );
  }
}

// app.js
class App extends React.Component {}

const mapStateToProps = state => {
  return {nav: state.router};
};

export default connect(mapStateToProps)(App);

例子通过 connect 的方式将 store 数据连接到组件中,在 state 变化的时候,组件的 mapStateToProps 会被调用并重新计算出一个新的 stateProps 来更新当前组件数据。connect 做了两件事情,一是把组件需要的 store 对象树属性 map 到当前组件中,二是把组件需要的数据结构在这个 map 函数中以 plain Object 返回,这做可以方便的定制当前组件需要的数据从而避免了层层 store 数据的嵌套。

要理解 connect 是如何工作的,需要先理解以下两点:

  • React 的 context
  • Provider

理解 react 的 context

旧版 context 的一个简单使用例子如下:

// 顶层组件
class Root extends Component {
    getChildContext() {
        return {
            text: 'xxx'
        }
    }
    render() {
        return <Parent />
    }
}

// 中间层组件
class IntermediateC extends Component {
    render() {
        return <Child />
    }
}

// 需要数据的组件
class Child extends Component {
    render() {
        return this.context.text
    }
}

旧版 context 不用在每个中间层组件中都显示地将 props 设置到子组件的属性中,通过顶层组件中的 getChildContext() 方法设置需要返回的数据即可。

新版 context 用 Provider、Consumer 对来实现,Provider 用来包裹顶层组件,Consumer 用来包裹底层获取数据的组件。但是获取数据的组件有多个数据来源,那么以上的嵌套地域又将重现。关于新旧版的 context API 在使用上有很大区别,但本质上都是解决同样的问题,即解决跨组件数据传递的问题,只是新版 context API 更符合 react 风格。

具体可以参见 https://reactjs.org/docs/context.html

Provider 的工作原理

Provider 查看源码其提供了3个方法:getChildContext、constructor、render,其中构造函数获取 props.store 供组件内部使用,render 方法返回一个 react 子元素,源码为 return Children.only(this.props.children),其中的 Children.only 是 react 提供的方法,this.props.children 表示的是一个 Provider 的子组件。

Provider 是一个容器组件, 从源码看做的事情很简单,即把嵌套的内容原封不动地作为子组件给渲染出来,最重要的是把 props.store 放到 context,从而子组件 connect 的时候都可以获取到。

connect 底层工作原理

要从 connect 源码看的确复杂了,这里不再逐行解析源码。我们来继续看看 connect 的使用:
connect(...args)(Component)

通过在根组件 Provider 组件设置 store 作为整个顶层组件的 context,其下所有子孙节点组件都可以获取到这个 store 对象树的数据。那接下来 connect 做的事情其实就很明了了,即 connect 首先执行的是一个 HOC,在这个高阶组件中,connect 接下来把 mapStateToProps 和 mapDispatchToProps 里的返回的属性,连同通过 context 获取到的 store 一起,过滤包装 store 数据最终传递给了被包裹的组件,connect 不会修改传递给它的组件类,相反它返回一个新的、被连接的组件类供开发者使用。

最后 connect 通过 redux store 的 subscribe API 来监听数据的变化,通过 shallowEqual 对比之前组件缓存的 props 和新计算出的属性,来决定是否需要更新组件,即重新将 args 里边的 props 传递给第二个被传入的 Component,达到更新组件的目的。理解了 connect 的工作原理,那么接下来就知道在开发过程中需要注意什么了。

reference

@JTangming JTangming changed the title react-redux 之 connect 理解 react-redux 之 connect Aug 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant