2

I have looked into multiple sources trying to solve this problem but could not find any answers. I have a functional component <Dashboard /> which will display some information from an API.

I expected the component to first get into useEffect, execute the getData function and then display {devices} on the screen. What happens, though, is that the store state is updated, but the component not. The {devices} variable is always undefined. I don't think I understand how to access my state variable from reducers/all/dashboard.js with useSelector.

dashboard/index.js

import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";

import api from "../../services/api";

import * as DashboardActions from "../../store/actions/dashboard";

const Dashboard = (props) => {
  const dispatch = useDispatch();
  const devices = useSelector(state => state.device)

  useEffect(() => {
    async function getData() {
      const pathname = "/dashboard";

      await api
        .get(pathname)
        .then((res) => {
          dispatch(DashboardActions.setData(res.data));
        })
        .catch((res) => {
          console.log(res.response.data);
        });
    }
    getData();
    console.log("devices ue ", devices);

  }, [dispatch]);

  return (
    <div>
      <h1>Dashboard</h1>
      <span>{devices}</span>
    </div>
  );
};

export default Dashboard;

reducers/all/dashboard.js

const INITIAL_STATE = {
  devices: [],
};

function dashboard(state = INITIAL_STATE, action) {
  console.log("Action ", action)
  if ("DASHBOARD_SET_DATA" === action.type) {
    const data = action.data;
    console.log("Data: ", data.devices)
    state = { ...state, devices: data.devices };

    console.log("State ", state)
  }

  return state;
}

export default dashboard;

actions/dashboard.js

export function setData(data) {
  return {
    type: "DASHBOARD_SET_DATA",
    data,
  };
}

I would appreciate any help a lot.

Thanks in advance!

  • 1
    How does `devices` look when you do `console.log(devices)`? – Boussadjra Brahim Jan 31 '21 at 22:55
  • React hook callbacks are 100% synchronous, you are simply logging the `devices` value from when the hook callback is invoked, not whatever the *asynchronous* `getData` function may be doing. Use a separate `useEffect` to log any updates to the `devices` value. Also, are you sure you are accessing the correct state slice? I.E. is it `state.device`, is that the name used in the combined reducers? Seems if you are correctly accessing state then you'd at least see the initial state, `{ devices: [] }`. – Drew Reese Jan 31 '21 at 22:58
  • @BoussadjraBrahim it is undefined – Joao Pedro Barreto Jan 31 '21 at 23:01
  • try out `useEffect(() => {console.log("devices => ",devices);},[devices])` and let me know – Boussadjra Brahim Jan 31 '21 at 23:02
  • @DrewReese oh! I think the second part of your comment is definitely part of the problem. I thought you accessed directly the name of the attribute stored in the reducer. I think if I change it to 'dashboard' it should work. – Joao Pedro Barreto Jan 31 '21 at 23:07
  • If you can update your question to include how you combine your reducers into the "root" reducer passed to the store we can help verify that. :) – Drew Reese Jan 31 '21 at 23:08
  • @DrewReese could you please elaborate more on the first part of your question? I didn't understand what you meant. – Joao Pedro Barreto Jan 31 '21 at 23:08
  • Ah, that means if you are attempting to console log whatever state update happens ***in*** the effect that you can only ever see the state from the ***current*** render cycle, not what it may be updated to. See https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately for an explanation. I know it is specifically for the `useState` hook, but it's all still basic component lifecycle. – Drew Reese Jan 31 '21 at 23:10
  • Hey @BoussadjraBrahim, I think Drew solve my problem. I hadn't understood how to use the useSelector hook apparently. – Joao Pedro Barreto Jan 31 '21 at 23:10
  • @DrewReese thanks, man! I think I get it now! If you want to post the second part of your comment as an answer, I will be happy to set it as correct! – Joao Pedro Barreto Jan 31 '21 at 23:14
  • For that I think you should include how you combine your reducers in your question so the answer has some context to work from and would be more beneficial to others landing here later. Otherwise, take the "freebie". :) Cheers. – Drew Reese Jan 31 '21 at 23:17
  • @DrewReese I have made some changes to the question. Let me know if there is anything I can improve in the question! – Joao Pedro Barreto Jan 31 '21 at 23:25

1 Answers1

4

The react-redux useSelector hook is selecting state from your redux store state object.

If your dashboard reducer is combined into your root reducer, something like

const rootReducer = combineReducers({
  ... other reducers
  dashboard,
  ... other reducers
});

Then the devices state value should be accessed from state.dashboard.devices.

The update for your component:

const devices = useSelector(state => state.dashboard.devices)
Drew Reese
  • 165,259
  • 14
  • 153
  • 181