2

i used react 16+ and redux get jsonplaceholder fake data to assign posts state but not working. can't assign the state. how can i assign json values into state using concat method. i check lifecycle methods also but can't get the answer.

Reducer

import * as actiontypes from './actions';
import axios from 'axios';

const initalstate = {
    counter: 0,
    posts: []
};

const reducer = (state = initalstate, action ) => {
    switch (action.type) {
        case actiontypes.actionFetchPost:
            axios.get('https://jsonplaceholder.typicode.com/posts')
                .then(res => {
                    return {
                        ...state,
                        posts: state.posts.concat(res.data)
                    }
                });
            break;
        default :
            return state;
    }
};

export default reducer;
2
  • 2
    Use redux-thunk or redux-saga and do the fetch in an async way in your actions but, not in your reducers. Commented Dec 2, 2019 at 13:26
  • 1
    From the docs: Things you should never do in a reducer: Mutate its arguments; Perform side effects like API calls and routing transitions; Call non-pure functions, e.g. Date.now() or Math.random(). Besides that you are returning the new state in the .then() handler of your promise. It will not update your store at all. Also see Async Actions for ways to handle async actions with redux. Commented Dec 2, 2019 at 13:30

2 Answers 2

5

Redux reducers must be pure functions, it means they should not contain any side effects like calling api.

You need to call api in action creators using redux-thunk package.

Codesandbox

An example action creator:

import {
  FETCH_POSTS_STARTED,
  FETCH_POSTS_FAILURE,
  FETCH_POSTS_SUCCESS
} from "./actionTypes";
import axios from "axios";

export const fetchPosts = () => {
  return dispatch => {
    dispatch(fetchPostsStarted());

    axios
      .get("https://jsonplaceholder.typicode.com/posts")
      .then(res => {
        dispatch(fetchPostsSuccess(res.data));
      })
      .catch(err => {
        dispatch(fetchPostsFailed(err.message));
      });
  };
};

const fetchPostsStarted = () => {
  return {
    type: FETCH_POSTS_STARTED,
    payload: {
      isLoading: true
    }
  };
};

const fetchPostsSuccess = posts => {
  return {
    type: FETCH_POSTS_SUCCESS,
    payload: {
      posts
    }
  };
};

const fetchPostsFailed = error => {
  return {
    type: FETCH_POSTS_FAILURE,
    payload: {
      error
    }
  };
};

And reducer file:

import {
  FETCH_POSTS_STARTED,
  FETCH_POSTS_SUCCESS,
  FETCH_POSTS_FAILURE
} from "../actions/actionTypes";

const initialState = {
  posts: [],
  loading: false,
  error: null
};

export default function(state = initialState, action) {
  switch (action.type) {

    case FETCH_POSTS_STARTED:
      return {
        ...state,
        loading: true
      };
    case FETCH_POSTS_SUCCESS:
      return {
        ...state,
        loading: false,
        error: null,
        posts: action.payload.posts
      };
    case FETCH_POSTS_FAILURE:
      return {
        ...state,
        loading: false,
        error: action.payload.error
      };
    default:
      return state;
  }
}

In store we use redux-thunk like this:

import { createStore, compose, applyMiddleware, combineReducers } from "redux";
import reduxThunk from "redux-thunk";
import postsReducers from "./reducers/postsReducers";

const rootReducer = combineReducers({
  posts: postsReducers
});

const store = createStore(rootReducer,  compose(applyMiddleware(reduxThunk)));

export default store;

Posts component:

import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchPosts } from "./store/actions/postsActions";

class Posts extends Component {
  componentDidMount() {
    this.props.fetchPosts();
  }

  render() {

    const { posts, loading, error } = this.props;

    return (
      <div>
        {loading && <div>LOADING...</div>}
        {error && <div>{error}</div>}
        <ul>
          {posts.map(post => (
            <li key={post.id}>{post.title}</li>
          ))}
        </ul>
      </div>
    );
  }
}

const mapStateToProps = state => {
  const { posts, loading, error } = state.posts;
  return {
    posts,
    loading,
    error
  };
};

export default connect(
  mapStateToProps,
  {
    fetchPosts
  }
)(Posts);

Index.js

import ReactDOM from "react-dom";
import store from "./store/store";
import { Provider } from "react-redux";
import Posts from "./Posts";

function App() {
  return (
    <div className="App">
      <Posts />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
);

Sign up to request clarification or add additional context in comments.

Comments

0

Do api call in action and return promise, use redux-thunk and redux-promise-middleware:

export const myApiCall = (args1, arg2) => async (dispatch, getState) => {
  const payload = fetch({ ...config });
  return dispatch({ type: 'MY_API_CALL', payload });
}

Then in reducer will have to handle two results: MY_API_CALL_FULFILLED and MY_API_CALL_REJECTED

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.