How to handle null state exception for map in reactJs

Viewed 83

I have this code which his basic todo list that I am trying to build in react

const Home = (props) => {
  const classes = useStyles();
  const [addNewTask, setAddNewTask] = useState(false);
  const [formData, setFormData] = React.useState({ taskText: "" });
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(getTasks());
  }, [dispatch]);

  const todos = useSelector((state) => state.todos);

  const handleAddNew = () => {
    setAddNewTask(addNewTask ? false : true);
  };

  const handleChange = (e) => {
    setFormData({ ...formData, [e.target.name]: e.target.value });
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(formData);
    dispatch(createTask(formData));
    dispatch(getTasks())
  };

  return (
    <Grow in>
      <Container>
        <Grid
          className={classes.adjustTop}
          container
          justify="space-between"
          alignItems="stretch"
          spacing={3}
        >
          <Grid item xs={2} sm={2}></Grid>
          <Grid item xs={8} sm={8}>
            <Typography variant="h2">TODO LIST</Typography>

            {todos==null?
            <Typography>Loading...</Typography>
            :
            <>
            {todos?.tasks?.map((todo) => (
              <Task id={todo.task} todo={todo} />
            ))}
            </>
            }
            
            
            <div style={{ textAlign: "center" }}>
              {addNewTask ? (
                <>
                  <Button
                    variant="contained"
                    size="large"
                    color="error"
                    style={{ marginTop: "10px" }}
                    onClick={handleAddNew}
                  >
                    Cancel Adding task
                  </Button>
                  <form style={{ paddingTop: "20px" }} onSubmit={handleSubmit}>
                    <Input
                      name="taskText"
                      label="Task Text"
                      handleChange={handleChange}
                      type="text"
                      placeholder="text"
                    />
                    <br />
                    <Button type="submit">Submit</Button>
                  </form>
                </>
              ) : (
                <Button
                  variant="contained"
                  size="large"
                  color="primary"
                  style={{ marginTop: "10px" }}
                  onClick={handleAddNew}
                >
                  + Add new task
                </Button>
              )}
            </div>
          </Grid>
          <Grid item xs={2} sm={2}></Grid>
        </Grid>
      </Container>
    </Grow>
  );
};

export default Home;

This is the reducer

import { FETCH_ALL,CREATE } from '../constants/actionTypes';

export default (state = {tasks:null}, action) => {
  switch (action.type) {
    case FETCH_ALL:
      return {...state,tasks:action.payload,errors:null}
    case CREATE:
      return {...state,tasks:action.payload,errors:null}  
    default:
      return state;
  }
};

& actions

import { FETCH_ALL,CREATE} from '../constants/actionTypes';

import * as api from '../api/index.js';

export const getTasks = () => async (dispatch) => {
  try {
    const { data } = await api.fetchTasks();
    console.log(data);

    dispatch({ type: FETCH_ALL, payload: data });
  } catch (error) {
    console.log(error.message);
  }
};


export const createTask = (taskText) => async (dispatch) => {
    try {
      const { data } = await api.createTask(taskText);
      dispatch({type:CREATE,payload:data})
    } catch (error) {
      console.log(error.message);
    }
  };

I am able to add the data to database on submit using handleSubmit tot the database but the issue is after each submit its giving me an error TypeError: _todos$tasks.map is not a function

I tried to handle this by using ternary operator & rendering Loading text on null & also have used chaining operator,but still getting same error

1 Answers

You are trying to get todos from your redux store

const todos = useSelector((state) => state.todos);

But there is no todos in your state and plus you are updating another state tasks in your reducer:

export default (state = {tasks:null}, action) => {
 switch (action.type) {
   case FETCH_ALL:
  return {...state,tasks:action.payload,errors:null}
   case CREATE:
  return {...state,tasks:action.payload,errors:null}  
   default:
  return state;
  }
 };
Related