简体   繁体   中英

How to configure mapDispatchToProps with react redux?

Below is my container code:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchMovies } from './actions';

export class Container extends Component {
  componentDidMount() {
    this.props.fetchMovies();
  }
}

const mapStateToProps = state => {
  return {
    movies: state.movies,
  };
};

function mapDispatchToProps(dispatch) {
  return {
    fetchMovies: bindActionCreators({fetchMovies}, dispatch),
  };
}

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

Action:

export const fetchMovies = () => dispatch => {
  console.log('fetchMovies called');
  // const movieResponse = fetchAPI(apiUrl);
  // console.log('movieResponse => ', JSON.stringify(movieResponse));
  dispatch({ type: actionTypes.FETCH_MOVIES, payload: [] });
};

Store:

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from '../rootReducer';

const middlewares = [thunk];

const store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(...middlewares))
);

export default store;

Dependency Versions:

"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-bootstrap": "^1.0.0-beta.11",
"react-dom": "^16.8.6",
"react-flexbox-grid": "^2.1.2",
"react-redux": "^7.1.0",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1",
"react-scripts": "3.0.1",
"redux": "^4.0.4",
"redux-actions": "^2.6.5",
"redux-promise-middleware": "^6.1.1",
"redux-thunk": "^2.3.0",

Getting error:

TypeError: this.props.fetchMovies is not a function

I am using latest react version of 16.8. Is that the reason? or am i missing something in configuration?

🎈 It is much more simple.

I don't know why you are using "thunk", I would suggest "saga" instead. I don't like the idea of adding some logic (fetching) to the actions, but any way here is your solution.

Container:

import React, { Component } from "react";
import connect from "./connect";

export class MovieList extends Component {
  componentDidMount() {
    this.props.fetchMovies();
  }

  render() {
    const { isFetching, movies } = this.props;
    const toLi = (movie, key) => <li key={key}>{movie.title}</li>;

    return isFetching
      ? <span>Data is loading</span>
      : <ul>{movies.map(toLi)}</ul>
  }
}

export default connect(MovieList); // pay attention to this

Binding container with store, this way will keep your container much like component:

import { connect } from "react-redux";
import { fetchMovies } from "../actions";

const props = state => ({
  movies: state.movies,
  isFetching: state.isFetching
});

const actions = {
  fetchMovies
};

export default connect(
  props,
  actions
);

Actions file:

export const FETCH_MOVIES = `MOVIE/FETCH_MOVIES`;
export const FETCHED_MOVIES = `MOVIE/FETCHED_MOVIES`;
export const FETCH_ERROR = `MOVIE/FETCH_ERROR`;

export const receivedMovies = movies => ({
  type: FETCHED_MOVIES,
  movies
});

export const onError = error => ({
  type: FETCH_ERROR,
  error
});

export const fetchMovies = () => dispatch => {
  dispatch({type: FETCH_MOVIES})

  return fetch(API_URL)
    .then(res => res.json())
    .then(data => dispatch(receivedMovies(data)))
    .catch(err => dispatch(onError(err)));
}  

🧐 A full working demo is here: https://codesandbox.io/s/7h56r

You are using bindActionCreators wrong. It takes an object as the first argument. The properties of the object are the action creators themselves.

And returns an object just like mapDispatchToProps does.

This might work

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ fetchMovies }, dispatch)
}

Or you might not use bindActionCreators at all

function mapDispatchToProps(dispatch) {
  fetchMovies: () => fetchMovies(dispatch)
}

There is a slight mismatch when using the bindActionCreators function. Try like this

const matchDispatchToProps = dispatch => bindActionCreators({
    fetchMovies
}, dispatch)

I don't use mapDispatchToProps i do it directly and it works perfectly

export default connect(
  mapStateToProps,
  { fetchMovies }
)(Container);

You can do it two ways

const mapDispatchToProps = (dispatch) => ({
  fetchMovies: () => dispatch(fetchMovies())
})

or

const mapDispatchToProps = (dispatch) => ({
  fetchMovies: bindActionCreators(fetchMovies, dispatch)
})

Also, make sure you're importing the connected Container

import Container from './container'

instead of

import { Container } from './container'

From further look into your code, your actions is not a pure function, hence it led to another error. Here's a quick sample fix for your code

You need to call it like this:

this.props.actions.fetchMovies()

You may also need to update your mapDispatchToProps method. We call ours like such:

const actions = {
 fetchMovies: fetchMoviesActionFunction, // or whatever your function is called here
};

const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(actions, dispatch) });

I'd go another way. It's been a while since I worked with thunk last time, but I did not use mapDispatchToProps alike methods. See if this approach does the trick for you

export class Container extends Component {
  componentDidMount() {
    this.props.dispatch(fetchMovies());
  }
}

const mapStateToProps = state => {
  return {
    movies: state.movies,
  };
};

export default connect(
  mapStateToProps
)(Container);

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