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 #11

Open
RachelRen opened this issue Aug 5, 2018 · 0 comments
Open

动手写react-redux #11

RachelRen opened this issue Aug 5, 2018 · 0 comments

Comments

@RachelRen
Copy link
Owner

RachelRen commented Aug 5, 2018

我们已经有了redux,redux做到了统一管理数据,现在我们也已经可以把redux应用到react中了。但是需要在最外层容器去初始化store,然后将state的属性以props层层传递下去。

redux做的事情(最重要的部分,即createStore这个文件)

  1. createStore
  2. subscribe: 这是事件监听模式的关键。相当于观察者模式中,去添加观察对象。每当dispatch时,去查看观察者对象中的变化。
  3. dispatch: 用于修改数据。接受一个action,然后去执行reducer。reducer的功能就是根据这个action和已知的state,生成一个新的state
  4. replaceReducer
  5. getState

Redux 与 react-redux的关系

所以我们需要想办法来解决这个层层传递数据的这个问题。那么子组件怎么拿到最外层组件store的数据就是关键了。

幸好有一个context:

context的作用,可以使子组件与父组件之间的数据传递,不用一层层往下传,可以使用context,来获取。但是他的问题是,过度依赖context,会使代码的复用率不高。

那我们的问题就变成:

  1. 需要解决的是context和store的联系
  2. 但并不是每个组件都一定要个store打交道的,怎样保证代码的复用率也是一个问题

在这里引入了下面的这个每个跟store打交道的组件,都需要用到context,如果有个页面不需要用到这些属性,那么这个组件就不能用了。所以需要把这个context抽取出来。可以使用高阶组件。

解决context和store的联系是,把所有的数据都放在store里面,然后用context来贯穿始终,这样子组件就可以通过context来获取store中的数据了。

class Index extends Component {
  static childContextTypes = {
    store: PropTypes.object
  }

  getChildContext () {
    return { store }
  }

  render () {
    return (
      <div>
        <Header />
      </div>
    )
  }
}

class Header extends Component {
  static contextTypes = {
    store: PropTypes.object
  }

  constructor () {
    super()
    this.state = { color: '' }
  }

  componentWillMount () {
    this._updateThemeColor()
  }
  _updateThemeColor () {
    const { store } = this.context
    const state = store.getState()
    this.setState({ color: state.color })
  }

  render () {
    return (
      <h1 style={{ color: this.state.color }}>Text</h1>
    )
  }
}

上面的方法解决了context与store的联系,但是之后每个组件都需要写相同的逻辑,先获取context,然后取出store,再从store中获取相应的值。这个重复率有点高,而且都是一样的逻辑。这样我们就可以用高阶组件来解决这个问题。

高阶组件

其实就是为了组件之间的代码复用。组件可能有着某些相同的逻辑,把这些逻辑抽离出来,放到高阶组件中进行复用。高阶组件内部的包装组件和被包装组件之间通过 props 传递数据。

代码复用的方法、形式有很多种,你可以用类继承来做到代码复用,也可以分离模块的方式。它其实就是设计模式里面的装饰者模式。它通过组合的方式达到很高的灵活程度。

已知在react-redux中,我们使用container组件来实现与store数据的传输

function mapStateToProps(state){
    return {
        loaded: state.XX,
    }
}
export default connect(mapStateToProps)(App);

这里的connect就是一个高阶组件

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export const connect = (mapStateToProps) => (WrappedComponent) => {
  
  export default connect(mapStateToProps)(App);

  class Connect extends Component{
    static contextType = {
      store: PropTypes.object
    }
    constructor () {
      super()
      this.state = {
        allProps: {}
      }
    }
    componentWillMount(){
      const { store } = this.context
      this._updateProps()
      store.subscribe(() => this._updateProps())
    }

    _updateProps() {
      const { store } = this.context
      let stateProps = mapStateToProps
        ? mapStateToProps(store.getState(), this.props)
        : {} // 防止 mapStateToProps 没有传入
      let dispatchProps = mapDispatchToProps
        ? mapDispatchToProps(store.dispatch, this.props)
        : {} // 防止 mapDispatchToProps 没有传入
      this.setState({
        allProps: {
          ...stateProps,
          ...dispatchProps,
          ...this.props
        }
      })
    }
    render(){
      const { store} = this.context;
      let stateProps = mapStateToProps(store.getState())

      return <WrappedComponent {...this.state.allProps} />
    }
  }

  return Connect
}

解决了子组件中的context,那来看一下父组件中的context。

export class Provider extends Component {
  static propTypes = {
    store: PropTypes.object,
    children: PropTypes.any
  }

  static childContextTypes = {
    store: PropTypes.object
  }

  getChildContext () {
    return {
      store: this.props.store
    }
  }

  render () {
    return (
      <div>{this.props.children}</div>
    )
  }
}

Provider就是简单的把context重新封装了一下,然后把其他属性原封不动的传给子组件。

总之一句话:

  • connect: 将store作为props注入
  • Provider: 使store在子孙组件的connect中能够获取到。
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