Using compose() and connect() together in React JS

2020-02-09 07:32发布

问题:

I am starting to develop a web application using React JS. I bought a theme from theme forest. In the theme, they are using compose like this in the component.

...Other code here
 Login.propTypes = {
      classes: PropTypes.shape({}).isRequired,
      width: PropTypes.string.isRequired
    };

    export default compose(withWidth(), withStyles(themeStyles, { withTheme: true }))(Login);

As you can see their code is using the compose at the end when exporting the Component. I cannot modify their built-structure. What I like to do now is I like to use connect feature of the react as well.

Normally connect is used in the place of compose. Now, if I want to use connect to work with the state of the application, how can I use it together with compose ?

回答1:

const enhance = compose(
    withRouter,
    withStyles(styles, 'some style'),
    connect(mapStateToProps, mapDispatchToProps),
    ....

export default enhance(MyComponent);


回答2:

import {bindActionCreators} from 'redux';
import compose from 'recompose/compose';
import { connect } from 'react-redux';
...Other code here
function mapStateToProps(state) {
    return {
        //return state
    }
}
function mapDispatchToProps(){
    return bindActionCreators({
        //actions
    }, dispatch);
}
Login.propTypes = {
    classes: PropTypes.shape({}).isRequired,
    width: PropTypes.string.isRequired
};
export default compose(withWidth(), withStyles(styles, {withTheme: true}), connect(mapStateToProps, mapDispatchToProps))(Login);

I hope this solves your problem.



回答3:

compose doesn't get rid of the pattern of passing a function to the result of a function call, but it reduces its use to one.

Only one HOC, no gain from using compose:

// withStyles, without compose
export default withStyles(styles)(MyComponent)

// withStyles, with compose
export default compose(withStyles(styles))(MyComponent)

// connect, without compose
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

// connect, with compose
export default compose(connect(mapStateToProps, mapDispatchToProps))(MyComponent)

Note that starting another function call immediately after a function call, which only recently came into fashion, is still there with compose.

With two HOCs there is a gain from compose because the nesting of parens is less:

// two HOCs, without compose
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(MyComponent))

// two HOCs, with compose
export default compose(connect(mapStateToProps, mapDispatchToProps), withStyles(styles))(MyComponent)

This still can be hard to follow if you aren't used to a nameless function being called immediately after it's created. If you prefer you can give a name to it:

// two HOCs, with compose
const enhance = compose(connect(mapStateToProps, mapDispatchToProps, withStyles(styles));
// export default enhance(MyComponent);

I prefer to use compose when there is more than one HOC, and to use it directly. I think cutting down on the nesting is useful but giving it a generic name like enhance is unnecessary.



回答4:

import {connect} from "react-redux";
import {compose} from 'redux'

class BookList extends Component {
    componentDidMount() {
        const {bookstoreService} = this.props;
        const data = bookstoreService.getBooks();
        this.props.booksLoaded(data);
    }

    render() {
        const {books} = this.props;
        return (
            <ul>
                {books.map(book => {
                    return (
                        <li key={book.id}>
                            <BookListItem book={book}/>
                        </li>
                    );
                })}
            </ul>
        );
    }
}

const mapStateToProps = ({books}) => {
    return {books};
};
const mapDispatchToProps = dispatch => {
    return {
        booksLoaded: newBooks => {
            dispatch(booksLoaded(newBooks));
        }
    };
};
export default compose(withBookstoreService(), connect(mapStateToProps, mapDispatchToProps))(BookList);



回答5:

I was receiving the following error while using compose with Typescript: JSX element type 'App' does not have any construct or call signatures. TS2604

I had to add ComponentType in the following way to make it work:

export default compose<ComponentType>(withTranslation(), connect(mapStateToProps, mapDispatchToProps))(App);