mert erdoğan
mert erdoğan

Reputation: 13

I can't fetch the data from reducer to component

I'm trying pass the data from reducer to component and receive as props.

But the data return UNDEFİNED, so I have tried console the data on reducer and action, but it's okey. There isn't any problem with the data coming from the API, but it always return to component undefined. Where is my fault?

Action

export default ProfileTab;

import axios from 'axios';
import { BASE, API_KEY } from '../config/env';
export const FETCHED_MOVIES = 'FETCHED_MOVIES';

export function fetchMovies() {
  return (dispatch) => {
    axios
      .get(`${BASE}s=pokemon&apikey=${API_KEY}`)
      .then((result) => result.data)
      .then((data) =>
        dispatch({
          type: FETCHED_MOVIES,
          payload: data.Search,
        }),
      );
  };
}

Reducer

import { FETCHED_MOVIES } from '../actions/movies';

const initialState = {
  fetching: false,
  fetched: false,
  movies: [],
  error: {},
};

export default (state = initialState, action) => {
  switch (action.type) {
    case 'FETCHED_MOVIES':
      return {
        ...state,
        movies: action.payload,
      };
    default:
      return state;
  }
};

Component

import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import { fetchMovies } from '../../actions/movies';

class Case extends Component {
  static propTypes = {
    movies: PropTypes.object.isRequired,
  };

  constructor(props) {
    super(props);
  }

  componentDidMount() {
    this.props.fetchMovies();
  }

  onChangeHandler = (e) => {
    this.setState({
      input: e.target.value,
    });
  };

  render() {
    console.log(this.props.movies);

    return (
      <div>
        <div className="movies-root">
          <div className="movies-wrapper">
            <div className="movies-container safe-area">
              <h1>mert</h1>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

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

const mapDispatchToProps = {
  fetchMovies,
};

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

Upvotes: 1

Views: 2103

Answers (3)

OfirD
OfirD

Reputation: 10510

Although there's already an accepted answer, I'm not sure how correct it is, as it's completely valid to pass mapDispatchToProps the way you did with the latest react (16.13.1) and react-redux (7.2.1) versions (I'm not sure about earlier versions).

Now, assuming your question contains the whole code, there are two important things missing:

  1. Creating the store:

    import { createStore } from "redux";
    const store = createStore(reducer);
    

    and passing it to the Provider component:

    <Provider store={store}>
    
  2. If you go ahead and do as above, you'll see that this.props.fetchMovies emits the following error:

    Actions must be plain objects. Use custom middleware for async actions.

    To fix it, do as it says and add a middleware, e.g. thunk:

    import { createStore, applyMiddleware } from "redux";
    import thunk from "redux-thunk";
    const store = createStore(rootReducer, applyMiddleware(thunk));
    

What follows is the full code. Note that I "split" fetchMovies into two functions: sync and async, for illustrating the difference usage between the two. I also modified your code (made is shorter, mostly) for this answer's readability. You can also see a live demo here:

File app.js

import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchMoviesSync, fetchMoviesAsyncMock } from "./api";

class App extends Component {
  componentDidMount() {
    this.props.fetchMoviesSync();
    this.props.fetchMoviesAsyncMock();
  }
  render() {
    return (
      <div>
        <div className="movies-root">
          <div className="movies-wrapper">
            <div className="movies-container safe-area">
              {this.props.movies.join("\n")}
            </div>
          </div>
        </div>
      </div>
    );
  }
}

const mapStateToProps = (state) => ({ movies: state.movies });
const mapDispatchToProps = {
  fetchMoviesSync,
  fetchMoviesAsyncMock
};

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

File api.js

export const FETCHED_MOVIES = "FETCHED_MOVIES";

export const fetchMoviesSync = () => ({
  type: FETCHED_MOVIES,
  payload: ["movie1", "movie2", "movie3", "movie4"]
});
export const fetchMoviesAsyncMock = () => (dispatch) => {
  dispatch({
    type: FETCHED_MOVIES,
    payload: ["movie5", "movie6", "movie7", "movie8"]
  });
};

File reducer.js

const initialState = {
  movies: [],
};

export default (state = initialState, action) => {
  switch (action.type) {
    case "FETCHED_MOVIES":
      return {
        ...state,
        movies: state.movies.concat(action.payload)
      };
    default:
      return state;
  }
};

File index.js

import React from "react";
import ReactDOM from "react-dom";
import Case from "./app";
import reducer from "./reducer";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";

let store = createStore(reducer, applyMiddleware(thunk));
ReactDOM.render(
  <Provider store={store}>
    <Case />
  </Provider>,
  document.getElementById("container")
);

File index.html

<body>
  <div id="container"></div>
</body>

Upvotes: 0

Dan
Dan

Reputation: 26

That's because your mapDispatchToProps function should return an object and take dispatch as parameter. Each field in your returned object should contain a function that dispatches your action.

So try something like this:

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

Upvotes: 0

D4rk4rmy
D4rk4rmy

Reputation: 76

Do this in the connect statement:

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

And remove the mapDispatchToProps function from your code.

Dispatching props as an object is quite incorrect. Try this, and it should work.

Upvotes: 1

Related Questions