简体   繁体   中英

Redux: Single container, multiple components

I'm quite new to both React and Redux, and I'm unsure about both best practices and technical solution to a case I'm working on. I'm using "component" and "container" as defined by Dan Abramov here .

The component I'm working on is a small collection of filter components: One input text field and two buttons, all filtering a list of entities. I've tried two approaches:

First approach: Single component containing three instances of two types of containers, containers connected to corresponding components.

This was what I first made. Here, the root component looks like the following:

import React, { PropTypes, Component } from 'react';
import Config from '../../config';
import FilterInput from '../containers/FilterInput';
import FilterLink from '../containers/FilterLink'

class FilterController extends Component {
    render() {
        return (
            <div className='filterController'>
                <FilterInput displayName="Search" filterName={Config.filters.WITH_TEXT} />
                <FilterLink displayName="Today" filterName={Config.filters.IS_TODAY} />
                <FilterLink displayName="On TV" filterName={Config.filters.ON_TV} />
            </div>
        )
    }
}
export default FilterController;

The two containers referenced here look pretty much as expected, as do the connected components. I'll show the FilterLink as an example:

import React, { PropTypes, Component } from 'react';
import {connect} from 'react-redux';
import {toggleFilter} from '../actions';
import FilterButton from '../components/filterbutton'

const mapStateToProps = (state, ownProps) => {
    return {
        active: !!state.program.filters[ownProps.filterName]
    }
}

const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        onClick: () => {
            dispatch(toggleFilter(ownProps.filterName, ownProps.input))
        }
    }
}

const FilterLink = connect(
    mapStateToProps,
    mapDispatchToProps
)(FilterButton)

export default FilterLink

And the corresponding FilterButton component:

import React, { PropTypes, Component } from 'react';

class FilterButton extends Component {

    render() {
        return (
            <button className={this.props.active ? 'active' : ''}
                    onClick={this.props.onClick}>
                {this.props.displayName}
            </button>
        )
    }
}

FilterButton.propTypes = {
    active: PropTypes.bool.isRequired,
    displayName: PropTypes.string.isRequired,
    onClick: PropTypes.func.isRequired,
    filterName: PropTypes.string.isRequired
};

export default FilterButton;

This approach works, but I'm thinking that it shouldn't be necessary to create two different containers. Which again lead me to my second attempt.

Second approach: single container containing multiple components.

Here, I made a larger container:

import React, { PropTypes, Component } from 'react';
import Config from '../../config';
import {connect} from 'react-redux';
import {toggleFilter} from '../actions';
import FilterButton from '../components/filterbutton'
import FilterInput from '../components/filterinput'

class FilterContainer extends Component {
    render() {
        const { active, currentInput, onChange, onClick } = this.props;
        return (
            <div className='filterController'>
                <FilterInput displayName="Search" filterName={Config.filters.WITH_TEXT} currentInput={currentInput} onChange={onChange} />
                <FilterButton displayName="Today" filterName={Config.filters.IS_TODAY} active={active} onClick={onClick}/>
                <FilterButton displayName="On TV" filterName={Config.filters.ON_TV} active={active} onClick={onClick}/>
            </div>
        )
    }
}

const mapStateToProps = (state, ownProps) => {
    return {
        active: !!state.program.filters[ownProps.filterName],
        currentInput: state.program.filters[ownProps.filterName] ? state.program.filters[ownProps.filterName].input : ''
    }
}

const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        onClick: () => {
            dispatch(toggleFilter(ownProps.filterName, ownProps.input))
        },
        onChange: newValue => {
            dispatch(toggleFilter(ownProps.filterName, newValue.target.value))
        }
    }
}

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(FilterContainer);

Here, all state interaction for the entire component is gathered in a single compoment. The components are the same here as in the first approach. This doesn't, however, really work: ownProps is empty in both mapStateToProps and mapDispathToProps. I may have misunderstood how the react-redux connection works.

So, given these things I have two questions: What's the best way to structure this component, in terms of containers and components? And secondly, why won't ownProps work similarily in the second approach as in the first?

Thank you for your time.

Not sure I have a specific answer regarding structure at the moment. As for "ownProps", that represents props that are specifically being passed in to a given component by its parent. Since you're connect() -ing FilterController, that means that "ownProps" would be coming from wherever you render that component, like: return <FilterController prop1="a" prop2={someVariable} /> .

Based on how you have those map functions written, it looks like you really want to be connecting the FilterInput and FilterButton components rather than FilterController.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM