Развязывающие компоненты React и Redux Connect

Как видно здесь Я стараюсь как можно больше отключить компоненты своего приложения и не сообщать им о каком-либо создателе хранилища или действия.

Цель состоит в том, чтобы заставить их управлять своим состоянием и функциями вызова, чтобы исправить изменения. Мне сказали, что вы делаете это с помощью реквизита.

Учитывая

// 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

И читаю здесь Я вижу умное использование Provider и connect, и моя реализация будет выглядеть примерно так:

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?

Ответ 1

В redux существует новый вид компонента, который называется контейнерами, это компонент, который использует connect (mapStateToProps, mapActionsToProps), чтобы передать состояние и действия текущему компоненту.

Все зависит от использования компонента. Например, если компонент Search 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 или действие различаются в глобальном состоянии. Лучший способ - передать эти свойства через другие контейнеры и сохранить компонент поиска чистым. Вот так:

// 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)

Для получения дополнительной информации ознакомьтесь с официальными документами об использовании redux с ответом.