简体   繁体   中英

Props not updating when I change the store in react

I have a problem, when I get my data from API and update the store the data doesn't changed. It is binded as a prop and I think it should changed, one more thing I noticed is that it doesn't call mapStateToProps after the store was updated. When i give some initial value to the store it displays it so I think it can see the store, something else is wrong obiviously but I can't figure out what.

Reducer code:

import { ADD_POST } from "../actions/addAction";
import { GET_POSTS } from "../actions/getAction";
import { DELETE_POST } from "../actions/deleteAction";
import { UPDATE_POST } from "../actions/updateAction";
import axios from "axios";

const initialState = {
  posts: []
};

export default function postsReducer(state = initialState, { type, payload }) {
  switch (type) {
    case ADD_POST:
      state = state.slice();
      state.push(payload);
      break;
    case GET_POSTS:
      axios
        .get("http://localhost:59511/api/post?date=31-12-2019")
        .then(response => {
          response.data.forEach(thePost => {
            state.posts = [...state.posts, thePost];
          });
          console.log(state.posts);
          return state;
        });
      break;
    default:
      return state;
  }

  return state;
}

index (here I am creating my store and wrapping the app component with provider):

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { combineReducers, createStore } from "redux";
import { Provider } from "react-redux";

import postReducer from "./reducers/postsReducers";

const allReducers = combineReducers(
  {
    post: postReducer
  },

  window.devToolsExtension && window.devToolsExtension()
);

const store = createStore(allReducers);

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

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.

serviceWorker.unregister();

Mapping it in my component like this:

const mapStateToProps = state => ({
  posts: state.post.posts
});

if you guys need anything else let me know, I have a file that is a bit large so I wouldn't like to add it if it's not neccessary, I am banging my head against the wall for a couple of hours now. Thanks in advance

===EDIT=== I also mapped my action to props

const mapActionToProps = {
  onDeletePost: deletePost,
  onUpdatePost: updatePost,
  onGetPost: getPosts
};

I have my action defined as

export const ADD_POST = "posts:addPost";

    export function addPost(newTitle, newHours, newDate) {
      return {
        type: ADD_POST,
        payload: {
          id: new Date().toString(),
          title: newTitle,
          hours: newHours,
          date: new Date().toLocaleDateString()
        }
      };
    }

So I already have the action defined there so I am not sure I need a dispatchToAction? I am looking it up as we speak and will try to make something, just a bit confused. ==END OF EDIT==

I think that technically your problem is that your reducer returns (after all of axios ) before the fetching is done. But that's not the problem you want to solve.

First of all, you have too much going on in your reducer. You shouldn't be implementing the action (fetching the data) in your reducer. I imagine in your component you're constructing an action that looks like {type: 'GET_POSTS'} , and then...dispatching it? Except you don't appear to be providing your component with a dispatch. So the action is never making it to the store. I can only assume because you haven't shown us where you're calling your action from.

You should be moving your fetching to its own async (thunk) action method:

   function getPosts() {
     return dispatch => {
       axios
        .get("http://localhost:59511/api/post?date=31-12-2019")
        .then(response => {
          const posts = response.data
          dispatch({type: 'GET_POSTS', payload: posts})
        });
     }
   }

And then simply add the posts to your state in your reducer:


export default function postsReducer(state = initialState, { type, payload }) {
  switch (type) {
    case GET_POSTS:
     return { ...state, posts: payload }
    default:
      return state;
  }

And then you'll have to connect the getPosts() function to your store using mapDispatchToProps . And you'll also have to use redux-thunk or this won't work at all.

You've got a good start with react-redux, but there's some gaps in your learning. You're going to need to look into async actions and redux thunk (or some other async action middleware). I'd suggest reviewing all the Redux documentation, mainly the advanced tutorials .


Your reducer is mutating state, and that's breaking the app.

In addition, you are making an AJAX call in a reducer, which is never allowed. All async logic happens outside reducers, and reducers only look at their state and action parameters to calculate the new state.

This why the first two "Essential" rules of the Redux Style Guide are Do Not Mutate State and Reducers Must Not Have Side Effects .

I'd strongly encourage you to use our new official Redux Toolkit package . Its configureStore() function sets up mutation detection by default, and it has functions like createSlice() which let you write simpler immutable update logic.

Beyond that, I'd suggest taking some more time to read through the Redux docs to understand how you are supposed to use Redux correctly.

I changed my action to be

import axios from "axios";
export const GET_POSTS = "posts:getPosts";

export function getPosts(theDate) {
  return dispatch => {
    axios
      .get("http://localhost:59511/api/post?date=31-12-2019")
      .then(response => {
        const posts = response.data;
        dispatch({ type: GET_POSTS, payload: posts });
      });
  };
}

reducer:

import { ADD_POST } from "../actions/addAction";
import { GET_POSTS } from "../actions/getAction";
import { DELETE_POST } from "../actions/deleteAction";
import { UPDATE_POST } from "../actions/updateAction";

const initialState = {
  posts: []
};

export default function postsReducer(state = initialState, { type, payload }) {
  switch (type) {
    case ADD_POST:
      state = state.slice();
      state.push(payload);
      break;
    case GET_POSTS:
      payload.forEach(element => {
        state.posts = [...state.posts, element];
      });

      break;
    default:
      return state;
  }

  return state;
}

in the component that I want to show posts I have:

const mapStateToProps = state => ({
  posts: state.post.posts
});

Then showing it with:

 render() {
     return (
       <div className="contentWindow">
         {this.props.posts.map((post, i) => {
           return (
           #some displaying logic

store creation changed with middleware:

const store = createStore(
  allReducers,
  applyMiddleware(thunk)
);

Still doesn't update my props in my component where I am mapping state to props. When I inspected the store with react dev tools changes are being made but some how my props weren't updated.

=====EDIT===== I have changed my reducer code to:

import { ADD_POST } from "../actions/addAction";
import { GET_POSTS } from "../actions/getAction";
import { DELETE_POST } from "../actions/deleteAction";
import { UPDATE_POST } from "../actions/updateAction";

const initialState = {
  posts: [123]
};

export default function postsReducer(state = initialState, { type, payload }) {
  switch (type) {
    case ADD_POST:
      state = state.slice();
      state.push(payload);
      break;
    case GET_POSTS:
      return { ...state, posts: payload };

    case DELETE_POST:
      state = state.filter(post => post.id !== payload.id);
      break;
    case UPDATE_POST:
      for (let index = 0; index < state.length; index++) {
        if (state[index].id === payload.theId) {
          state[index].id = payload.theId;
          state[index].date = payload.newDate;
          state[index].hours = payload.newHours;
          state[index].title = payload.newTitle;
        }
      }

      break;
    default:
      return state;
  }

  return state;
}

Now I have a problem that it's changing its initial state every time xD, I am glad I managed to get through the previous one btw feeling so strong now :D.

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