Redux useSelector gets called 3 times and only 3rd time returns the data list for .map()
Answer a question
i am struggling with getting data from my redux state. I am using saga to fetch lambda function that gives me a list of projects from a github account. everything works the projects are fetched and saved to the state. in my component i am using useSelector hook to get the projects from my state and display them using map().
The problem is that for some reason looks like the hook is called three times and the first two times the projects list isynt set yet and i get this error:
TypeError: Cannot read property 'map' of undefined
Projects Component:
import React, { useState, useEffect } from "react";
import "./Projects.scss";
import { useSelector } from "react-redux";
function Projects() {
const projects = useSelector((state) => state.projects);
const isLoading = useSelector((state) => state.isLoading);
console.log('PROJECTS', projects)
return (
<section className="Projects" id="work">
<h2 className="Projects__title Projects__title--portfolio">My Work</h2>
{ !isLoading ? projects.projects.map((project, index) => {
<div className="Projects__portfolio">
<p>HELLLO</p>
<a href={project.html_url} className={`Projects__portfolio--img${index}`}></a>
</div>
}) : null}
</section>
);
}
export default Projects;
Reducer:
import { ActionTypes } from "../types";
const initialState = {
projects: [],
isLoading: false,
error: null,
};
const projectsReducer = (state = initialState, action) => {
switch (action.type) {
case ActionTypes.GET_PROJECTS_REQUEST: {
return {
isLoading: true
}
}
case ActionTypes.GET_PROJECTS_SUCCESS: {
return {
isLoading: false,
projects: action.payload,
};
}
// case ActionTypes.GET_PROJECTS_FAILED:
// return {
// error: action.message,
// };
default: {
return state;
}
}
};
export default projectsReducer;
SAGA:
import { call, put, takeEvery, fork } from "redux-saga/effects";
import {ActionTypes } from '../types';
import * as actionProjects from '../actions';
import * as http from '../../api/httpClient';
function* fetchProjects() {
try {
const result = yield call(http.getProjects);
yield put(actionProjects.getProjectsSuccess(JSON.parse(result.data.body)));
} catch (error) {
console.log(error)
yield put({ type: "GET_PROJECTS_FAILED", message: error.message });
}
}
function* watchGetProjectsRequest() {
yield takeEvery(ActionTypes.GET_PROJECTS_REQUEST, fetchProjects);
}
const projectsSagaResult = [fork(watchGetProjectsRequest)];
export default projectsSagaResult;
This shows the console log from the component its self and as you can see it is called 3 times and only the third time it returns the list of projects and i guess this is why i get the TypeError: Cannot read property 'map' of undefined error 
Answers
First render, isLoading is false (because set as false in your initialState). So your !isLoading is true and so it tries the map which fails because projects is an empty array, so projects.projects do not exists (and so cannot map on it)
Second render because you're fetching your datas, so isLoading goes to true
And third render is when you get back your datas from backend.
Many ways to fix your error:
1/ set isLoading to true by default in your initialState
2/ use conditional chaining to check if array exists projects.projects?.map(....)
3/ in your reducer, do not store full payload but only projects to avoid unecessary nested datas projects: action.payload.projects
Pick the one your prefer :)
更多推荐
所有评论(0)