I am creating an app and using useContext+useReducer to manage my state. There is a functionality where I let user create a project and then append this project to projects array state and navigate user to the next screen where he can add more details about the project. I do this using the useNavigate hook from react-router-dom.
When I direct user to the next screen, I am trying to pass the project that he created by sending the last project in the projects array state.
createProject.jsx
import React, { useState, useContext, useReducer } from "react";
import FormField from "../components/formField";
import { useNavigate } from "react-router-dom";
import TextAreaField from "../components/TextFied";
import Button from "../components/button";
import ProjectContext from "../context/projects/projects-context";
import projectsReducer from "../context/projects/projects-reducer";
export default function CreateProjectPage() {
const [projectData, setProjectData] = useState({
project_name: "",
project_description: "",
});
const handleChange = (e) => {
const { name, value } = e.target;
setProjectData({ ...projectData, [name]: value });
};
const { addProject, projects } = useContext(ProjectContext);
//const [state,dispatch] = useReducer()
let navigate = useNavigate();
const handleSubmit = (e) => {
e.preventDefault();
if (!projectData["project_name"]) {
alert("Project Name cannot be empty");
return;
}
// Call the Create Project post Service now.
fetch("http://localhost:5000/project/createProject", {
credentials: "include",
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(projectData),
})
.then(async (res) => {
const response = await res.json();
if (res.status == 201) {
addProject(response); // This method dispatches the add project request and adds the new project which is in the response
// to the Projects State.
navigate("/add-project-details", {
state: { project: projects[projects.length - 1] },
});
} else {
}
})
.catch((err) => {
console.log(err);
});
setProjectData({
...projectData,
project_name: "",
project_description: "",
});
};
return (
<div>
<h2>Create Project</h2>
<div className="timeline">
<span className="active">1</span>
<span className="inactive">2</span>
</div>
{/* <h3 className="darkGrey mb-50">Basic Info</h3> */}
<form action="POST" onSubmit={handleSubmit}>
<div className="form-field-block">
<FormField
name="project_name"
type="text"
value={projectData["project_name"]}
onChange={handleChange}
label="Project Name"
/>
</div>
<div className="form-field-block">
<TextAreaField
name="project_description"
type="text"
label="Project Description"
onChange={handleChange}
value={projectData["project_description"]}
/>
</div>
<Button style="primary" type="submit" value="Create Project" />
</form>
</div>
);
}
Here is the addProject(response) calls a method in projectsState.jsx which in turn dispatches request to add project to the projects array.
projectsState.jsx
import { useEffect, useReducer } from "react";
import { SET_PROJECTS, ADD_PROJECT, ADD_PRIORITY } from "./projects-actions";
import projectsReducer from "./projects-reducer";
import ProjectsContext from "./projects-context";
const ProjectsState = (props) => {
const initialState = {
projects: [],
};
const [state, dispatch] = useReducer(projectsReducer, initialState);
const setProjects = (projects) => {
dispatch({
type: SET_PROJECTS,
payload: projects,
});
};
const addProject = (project) => {
dispatch({
type: ADD_PROJECT,
payload: project,
});
};
const addPriority = (priorityObj) => {
dispatch({
type: ADD_PRIORITY,
payload: priorityObj,
});
};
return (
<ProjectsContext.Provider
value={{ projects: state.projects, addProject, setProjects, addPriority }}
>
{props.children}
</ProjectsContext.Provider>
);
};
export default ProjectsState;
projectReducer.jsx
import { SET_PROJECTS, ADD_PROJECT, ADD_PRIORITY } from "./projects-actions";
import { getProjectObj, addPriorityToProject } from "./project-util";
import Project from "./project-class";
const projectsReducer = (state, action) => {
switch (action.type) {
case SET_PROJECTS:
const projectObjs = [];
// Create project objects and add it in the projects array
for (const project of action.payload) {
const projectObj = getProjectObj(project);
projectObjs.push(projectObj);
}
return {
projects: [...projectObjs],
};
case ADD_PROJECT:
// create Project Object
const projectObj = getProjectObj(action.payload);
console.log(projectObj);
return {
projects: [...state.projects, projectObj],
};
case ADD_PRIORITY:
// These lines of code are being called after the navigate statement in the createProject file (line 41).
// Because of which the user is being redirected to the next screen without the new project added.
let projects = addPriorityToProject(action.payload, state.projects);
console.log(projects);
return {
...state,
project: [...projects],
};
default:
return state;
}
};
export default projectsReducer;
But when I run the code after creating the project when user is getting redirected to additionalDetails page, the project that is being passed is not the latest one.
When I use debugger I found that the addProject gets called and then it calls the dispatcher but after that the control comes back to the createProject file and the useNavigate line is executed and then the control goes back and updates the project in the projects array. But by that time the user is already navigated to the next screen with incorrect project.
Can you please help me understand what I am doing wrong.
Thanks!