React using Redux, not saving application status

Asked

Viewed 480 times

1

I’m trying to implement redux in my app Web react, does not give any error but also does not work, when I try to capture state is as undefined

structure:

  • App.js
  • Routes.js
  • store/actions.js
  • store/index.js
  • store/reducers/index.js
  • store/reducers/posts.js
  • pages/Main.js

App.js

import React from 'react';
import './App.css';
import { Provider } from "react-redux";
import store from "./store";
import Routes from './routes';

function App() {
  return (
    <Provider store={store}>
      <Routes />
    </Provider> 
  );
}

export default App;

Routes.js

import React from 'react';
import { Switch, Route } from 'react-router-dom';
import { ConnectedRouter } from "connected-react-router";

import Main from './pages/Main';
import Detalhe from './pages/Detalhe';

import history from "./history";

export default function Routes(){
    return (        
        <ConnectedRouter history={history}>
        <Switch>
            <Route path="/" exact component={Main} />
            <Route path="/posts/:id" exact component={Detalhe} />
        </Switch>
        </ConnectedRouter>
    );
}

store/index.js

import { applyMiddleware, createStore, compose } from "redux";
import { connectRouter, routerMiddleware } from "connected-react-router";
import thunk from "redux-thunk";

import history from "../history";
import rootReducer from './reducers';

const initialState = {};

const middleware = [
    routerMiddleware(history),
    thunk
];


const store = createStore(
  connectRouter(history)(rootReducer),
  initialState,
  compose(
      applyMiddleware(...middleware)
  )
);

export default store;

store/actions.js

import { push } from "connected-react-router";

export const dataSave = (posts) => dispatch => ({

  type: "ADD_POSTS",
  payload: { posts }

});

store/reducers/index.js

import { combineReducers } from "redux";

import posts from "./posts";

export default combineReducers({
  posts
});

store/reducers/posts.js

export default function posts(state = [], action) {
    switch (action.type) {
      case "ADD_POSTS":
        return {...state, posts: action.payload.posts };        
      default:
        return state;
    }
  }

pages/Main.js

import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import './Main.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import Navbar2 from '../components/Navbar';
import {isMobile} from 'react-device-detect';
import api from '../services/api';

import { connect } from "react-redux";
import * as DataActions from "../store/actions";
import { bindActionCreators } from 'redux';

function Main({ history, match, dataSave, posts }){ 

   /* Nao e mais um state local */
   // const [posts, setPosts] = useState([]);

    let [page] = useState(1);

    /*
    if (localStorage.getItem("posicaoScroll") === undefined &&
        localStorage.getItem("posicaoScroll") === null
      ) {
        localStorage.setItem("posicaoScroll", JSON.stringify(0));
      }
    */  

    const [layout, setLayout] = useState(null);

    /*
    const [posicao, setPosicao] = useState(
        JSON.parse(localStorage.getItem("posicaoScroll"))
      );
    */  

    useEffect(() => {
      async function loadDados(){

        const response = await api.get('/posts?_start=0&_limit=10'); 

        //Agora seta via redux
        //setPosts(response.data);

        console.log('Get da API');
        console.log(response.data);

        dataSave(response.data); 

        //Teoricamente era para aparecer os posts no console agora
        console.log('Estado no Redux');
        console.log(posts);

        page = 1;

        if (isMobile) {
          setLayout('mob');   
        } else {    
          setLayout('web');
        }

        /*
        let scrollpos = localStorage.getItem("posicaoScroll");
        if (scrollpos !== undefined && scrollpos !== null) {
            // Timeout necessário para funcionar no Chrome 
            if (scrollpos <= 100)
            scrollpos = 0;
            console.log(scrollpos); //JSON.parse(scrollpos)
            setTimeout(function() {
            window.scrollTo(0, scrollpos);
            }, 1);
        } 
        */


      };

      loadDados();

    }, []);

    /*
    useEffect(() => {       
      // Verifica mudanças no Scroll e salva no localStorage a posição 
       window.onscroll = function(e) {
        setPosicao(window.scrollY);
        localStorage.setItem("posicaoScroll", JSON.stringify(posicao));
      }           

        return () => {};
      }, [posicao, page, setPosicao]);
    */


    useEffect(() => {
        window.addEventListener('scroll', handleScroll);
        return () => window.removeEventListener('scroll', handleScroll);
    }, []);


    /* Scroll infinito nao ta funcionando pq nao ta disparando evento ao chegar no final da pagina, talvez calculo esteja errado */
    async function handleScroll() {

        console.log(document.documentElement.scrollTop);

         //if (((document.documentElement.scrollTop) / (page * window.innerHeight)) / page >= 5.80) {
        if (((document.documentElement.scrollTop) / 1350) >= page) {

            page = page + 1;

            //Comentei para nao da erro
            fetchMoreListItems(page - 1);

        } else {
            return;
        }
    }  

    async function fetchMoreListItems(pagecount) {        

        const response = await api.get(`/posts?_start=${pagecount * 10}&_limit=10`);       

        //Agora seta via Redux
        //setPosts(prevState => ([...prevState, ...response.data]));    
        dataSave(response.data); // Isso é equivalente a linha acima?

    }

    //------

    return (
        <div className="main">           

        <Navbar2/>

               <div className="main-container">        

                    { posts.length > 0 ? (
                    <ul id="ullist" className={"main-container ul"+layout}> 
                        {posts.map(post => (
                            <Link to={`/posts/${post.id}`} >
                                <li id="lilist" className={"main-container li"+layout} key={post.id}>

                                    <img src='https://cdn.icon-icons.com/icons2/1378/PNG/512/avatardefault_92824.png'/>

                                    <footer>                                       
                                        <strong>{post.title}</strong>                                       
                                    </footer>

                                </li>
                            </Link>
                        ))}     
                    </ul>     
                    ) : (
                        <div className="empty">Nenhum Post :(</div>
                    ) }    
            </div>
        </div>
    );
}


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

const mapDispatchToProps = dispatch =>
bindActionCreators(DataActions, dispatch);


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

In the Main.js file when the loadDados() function executes,

she calls the action function:

dataSave(response.data); 

but when I execute:

console.log(posts);

stays as a Undefined :(

What am I doing wrong?

repository on github: https://github.com/felipeagger/React-Redux/tree/redux

Branch Redux

1 answer

1


I implemented your code in the Codesandbox, ignoring some missing parts.

Almost always, when using React-Redux, an action fired apparently has no effect on the store, it is because the dispatch is not being used. Of two, one:

1) you are importing into your component an action defined in the folder actions, without involving her in a dispatch, through mapDispatchToProps. That you did correctly.

2) you are not using the function dispatch within the Creator action. That was your case! You return the object that represents the action instead of dispatching it to the store. The correct is:

export const dataSave = posts => dispatch => {
    dispatch({
        type: 'ADD_POSTS',
        payload: { posts }
    })
}

In addition, some comments:

  • the action is sent to the store, which updates the component with the new value. However, do not expect this identifier to get the new value immediately after calling dataSave(response.data). What it does is ask the store to modify its state, and after a while React will react to this change and call the render method of this component with the new posts.
  • each Reducer must have an initial state that will evolve over time. How you used combineReducers, the initial state is state: { posts: [] }. However, Reducer posts will change the subtree posts, not the entire state. So, the correct one would be to use something like the following
// reducers/posts.js
const INITIAL_STATE = {
  list: []
};

export default function posts(state = INITIAL_STATE, action) {
  switch (action.type) {
    case "ADD_POSTS":
      return { ...state, list: action.payload.posts };
    default:
      return state;
  }
}

// pages/Main.js
const mapStateToProps = state => ({
  posts: state.posts.list
});

Browser other questions tagged

You are not signed in. Login or sign up in order to post.