Action and state shown on console but undefined on aplication with hooks react

Viewed 52

I am trying to do an application that read from an API and print on the screen the list of itens but is nor working and I am stucked on this for 5 days.

I did a codesandbox here with a Fake API: https://codesandbox.io/s/heuristic-wright-on220 and I explain below the problem going though the code

First I have created my store on index.js

const store = createStore(reducer, applyMiddleware(...middleware));

const App = () => {
  return (
    <Router>
      <Switch>
        <Route path="/" exact component={Dashboard} />
        <Route component={NoMatchRoute} />
      </Switch>
    </Router>
  );
};

I have created my state on metrics.js

 export type MetricState = {
  metrics: IMetric[];
};

And finally on Dashboard.tsx I print on the screen bu updating the state by doing an api call with 'fetchMetrics':

const catalog = useSelector<RootState, MetricState>(
    (state) => state.metricsList
  );

  const dispatch = useDispatch();

  const classes = useStyles();
  useEffect(() => {
    dispatch(appReducer.actionCreators.fetchMetrics());
  }, []);

Here the definition of fetchMetrics

   export function fetchMetrics() {
      const action = {
        type: ACTION_TYPES.FETCH_METRICS,
         payload: []
       };
      return fetchMetricsCall(action);
    }
    const fetchMetricsCall = (action) => async (dispatch) => {
      try {
        const res = await axios.get("/api/foo");
        dispatch({
          type: action.type,
          payload: res.data //contains the data to be passed to reducer
        });
      } catch (e) {
        dispatch({
          type: ACTION_TYPES.FAILURE,
          payload: console.log(e) //TODO: fix return type
        });
      }
    };
    
    export default {
      fetchMetrics
    };

On index.js I create the state and connect the provider

const store = createStore(
    rootReducer,
    applyMiddleware(...middleware)
)

const App = () => {

  return (
    <Router>
      <Switch>
        <Route path="/" exact component={Dashboard} />
        <Route component={NoMatchRoute} />
      </Switch>
    </Router>
  );
};

And this is my reducer:

const initialState: MetricState = {
  metrics: []
};

const fetchMetrics = (
  state = initialState,
  action: MetricAction
): MetricState => {
  switch (action.type) {
    case ACTION_TYPES.FETCH_METRICS:
      return {
        ...state,
        metrics: action.payload
      };
    case ACTION_TYPES.CHOOSE_METRIC:
      return {
        ...state,
        metrics: action.payload
      };
    default:
      return {
        ...state
      };
  }
};

const rootReducer = combineReducers({
  fetchMetrics
});

export default rootReducer;

My problem is that I just can't make it work. I have already read lots on stackoverflow questions and tutorials and I just cannot find my error. Does someone have an idea? On the image it's possible to see that the data is fetched from the real API but do not show on the screen.

Thank you! enter image description here

1 Answers

It doesn't look like state.metricsList actually exists inside the state, and inside the Dashboard component is (state) => state.metricsList. Your state only has fetchMetrics.metrics (combineReducer is going to add object onto the state with the key of the reducer's name)

So right now, the structure of your state looks like this:

state: { 
  fetchMetrics: { 
    metrics: [] 
  }
}

I think the main two things you need to change are:

metrics.ts

export type RootState = {
  fetchMetrics: MetricState;
};

Dashboard.tsx

const catalog = useSelector<RootState, MetricState>(
  (state) => state.fetchMetrics
);

In the code sandbox metrics is some weird HTML error, let me know if my answer doesn't work.

Related