解耦 React 组件和 Redux Connect

Decoupling React Components and Redux Connect

正如所见here我正在尝试尽可能多地分离我的应用程序的组件,并使它们不知道任何存储或动作创建者。

目标是让它们管理自己的状态并调用函数来发出变化。有人告诉我,你使用道具来做到这一点。

正在考虑

// Menu.jsx

import React from 'react'
import { className } from './menu.scss'
import Search from 'components/search'

class Menu extends React.Component {
  render () {
    return (
      <div className={className}>
        <a href='#/'>Home</a>
        <a href='#/foo'>foo</a>
        <a href='#/bar'>bar</a>
        <Search />
      </div>
    )
  }
}

// Search.jsx

import React from 'react'
import { className } from './search.scss'

class Search extends React.Component {
  render () {
    let { searchTerm, onSearch } = this.props

    return (
      <div className={`search ${className}`}>
        <p>{searchTerm}</p>
        <input
          type='search'
          onChange={(e) => onSearch(e.target.value)}
          value={searchTerm}
        />
      </div>
    )
  }
}

Search.propTypes = {
  searchTerm: React.PropTypes.string,
  onSearch: React.PropTypes.function
}

export default Search

阅读 here 我看到 Providerconnect 的巧妙用法,我的实现看起来像这样:

import { bindActionCreators, connect } from 'redux'
import actions from 'actions'

function mapStateToProps (state) {
  return {
    searchTerm: state.searchTerm
  }
}

function mapDispatchToProps (dispatch) {
  return bindActionCreators({
    dispatchSearchAction: actions.search
  }, dispatch)
}

export default connect(mapStateToProps, mapDispatchToProps)(Search)

假设我有一个商店处理 searchTerm 作为全局状态的一部分。

问题是,这段代码属于哪里?如果我把它放在 Search.jsx 中,我会将动作与组件结合起来,更重要的是 redux

我是否应该有两个不同版本的组件,一个是解耦的,一个是 connect()ed 的,并且 <Menu /> 可以使用它?如果是,我的文件树会是什么样子?每个组件一个文件或类似 make-all-connected.js ?

在 redux 中,存在一种称为容器的新组件,这是使用 connect(mapStateToProps, mapActionsToProps) 将状态和操作传递给当前组件的组件。

一切都取决于组件的用途。例如,如果您的组件 Search 仅用于相同的状态和操作,您的容器可能与您的组件相同,如下所示:

// Search.jsx
import { connect } from 'redux'
import actions from 'actions'
import React from 'react'
import { className } from './search.scss'

class Search extends React.Component {
  render () {
    let { searchTerm, onSearch } = this.props

    return (
      <div className={`search ${className}`}>
        <p>{searchTerm}</p>
        <input
          type='search'
          onChange={(e) => onSearch(e.target.value)}
          value={searchTerm}
        />
      </div>
    )
  }
}

Search.propTypes = {
  searchTerm: React.PropTypes.string,
  onSearch: React.PropTypes.function
}


function mapStateToProps ({searchTerm}) {
  return {
    searchTerm
  };
}

const mapDispatchToProps = {
    onSearch: actions.search
}

export default connect(mapStateToProps, mapDispatchToProps)(Search)

但是,如果您的计划是在另一个容器中重用此组件,并且 searchTerm 或操作在全局状态上有所不同。最好的方法是通过其他容器传递此属性,并保持 Search 组件纯净。像这样:

// Container1.jsx
import { connect } from 'redux'
import actions from 'actions'
import React, { Component } from 'react'

class Container1 extends Component {
  render() {
    const { searchTerm, handleOnSearch } = this.props;
    return (
      <div>
        <Search searchTerm={searchTerm} onSearch={handleOnSearch} />
      </div>
    ) 
  }
}

function mapStateToProps ({interState: {searchTerm}}) {
  return {
    searchTerm
  };
}

const mapDispatchToProps = {
  handleOnSearch: actions.search
}

export default connect(mapStateToProps, mapDispatchToProps)(Container1)

// Container2.jsx
import { connect } from 'redux'
import otherActions from 'otheractions'
import React, { Component } from 'react'

class Container2 extends Component {
  render() {
    const { searchTerm, handleOnSearch } = this.props;
    return (
      <div>
        <Search searchTerm={searchTerm} onSearch={handleOnSearch} />
      </div>
    ) 
  }
}

function mapStateToProps ({otherState: {searchTerm}}) {
  return {
    searchTerm
  };
}

const mapDispatchToProps = {
  handleOnSearch: otherActions.search
}

export default connect(mapStateToProps, mapDispatchToProps)(Container2)

有关详细信息,请阅读 official docs about using redux with react