I have divided my code into three different files for modularity.
- ProfessionalInfo.js -> that binds logic with UI.
- professionalInfoUI -> the Form UI
- useProfessionalInfoForm -> all of my logic goes here.
useProfessionalInfoForm:
// useProfessionalInfoForm.js
import { useEffect } from "react";
import { toast } from "react-toastify";
import { professionalInfoAPI } from "../../../../api/professionalInfo";
import useProfessionalInfoStore from "../../../../zustand/ProfessionalInfoStore";
const useProfessionalInfoForm = () => {
const professionalInfo = useProfessionalInfoStore(
(state) => state.professionalInfo
);
const setProfessionalInfo = useProfessionalInfoStore(
(state) => state.setProfessionalInfo
);
const resetProfessionalInfo = useProfessionalInfoStore(
(state) => state.resetProfessionalInfo
);
useEffect(() => {
if (!professionalInfo.workExperiences) {
setProfessionalInfo((state) => ({
...state,
workExperiences: [],
}));
}
}, []);
const handleChange = (e) => {
const { name, value } = e.target;
setProfessionalInfo((state) => ({
...state,
[name]: value,
}));
};
const handleWorkChange = (e, index) => {
const { name, value } = e.target;
setProfessionalInfo((state) => ({
...state,
workExperiences: state.workExperiences.map((exp, i) =>
i === index ? { ...exp, [name]: value } : exp
),
}));
};
const handleAddWorkExperience = () => {
setProfessionalInfo((state) => ({
...state,
workExperiences: [
...state.workExperiences,
{ start: null, end: null, work: "" },
],
}));
};
const handleRemoveWorkExperience = (index) => {
setProfessionalInfo((state) => ({
...state,
workExperiences: state.workExperiences.filter((_, i) => i !== index),
}));
};
const handleWorkDateChange = (date, index, fieldName) => {
setProfessionalInfo((state) => ({
...state,
workExperiences: state.workExperiences.map((exp, i) =>
i === index ? { ...exp, [fieldName]: date } : exp
),
}));
};
const handleSubmit = () => {
professionalInfoAPI(professionalInfo)
.then((response) => {
toast.success("Data has been sent!", {
position: toast.POSITION.TOP_RIGHT,
autoClose: 2000,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
});
// Optionally, you can reset the form after successful submission
resetProfessionalInfo();
})
.catch((error) => {
toast.error(
"An error occurred while sending data. Please try again later.",
{
position: toast.POSITION.TOP_RIGHT,
autoClose: 2000,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
}
);
console.error("Error:", error);
});
};
const handleReset = () => {
resetProfessionalInfo();
};
return {
professionalInfo,
handleChange,
handleWorkChange,
handleAddWorkExperience,
handleRemoveWorkExperience,
handleWorkDateChange,
handleSubmit,
handleReset,
};
};
export default useProfessionalInfoForm;
professionalInfoUI:
import React from "react";
import Grid from "@mui/material/Grid";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import { Button } from "@mui/material";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
const ProfessionalInfoUI = ({
professionalInfo,
handleChange,
handleWorkChange,
handleWorkDateChange,
handleAddWorkExperience,
handleRemoveWorkExperience,
handleSubmit,
handleReset,
}) => {
console.log('professionalInfoUi: ', professionalInfo)
return (
<>
<Typography variant="h6" gutterBottom>
Professional Information
</Typography>
<Grid container spacing={3}>
<Grid item xs={12} sm={6}>
<TextField
id="degree"
name="degree"
label="Degree"
fullWidth
variant="standard"
value={professionalInfo.degree || ""}
onChange={handleChange}
/>
</Grid>
{professionalInfo?.workExperiences?.map((workExp, index) => (
<Grid item xs={12} sm={6} key={index}>
<TextField
id={`work-${index}`}
name="work"
label={`Experience ${index + 1}`}
fullWidth
variant="standard"
value={workExp.work || ""}
onChange={(e) => handleWorkChange(e, index)}
/>
<DatePicker
selected={workExp.start}
onChange={(date) => handleWorkDateChange(date, index, "start")}
dateFormat="dd/MM/yyyy"
popperPlacement="bottom-start"
/>
<DatePicker
selected={workExp.end}
onChange={(date) => handleWorkDateChange(date, index, "end")}
dateFormat="dd/MM/yyyy"
popperPlacement="bottom-start"
/>
<Button onClick={() => handleRemoveWorkExperience(index)}>
Remove
</Button>
</Grid>
))}
<Grid item xs={12}>
<Button onClick={handleAddWorkExperience}>Add Work Experience</Button>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
id="cert"
name="cert"
label="Certification"
fullWidth
variant="standard"
value={professionalInfo.cert || ""}
onChange={handleChange}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
id="skill"
name="skill"
label="Skills"
fullWidth
variant="standard"
value={professionalInfo.skill || ""}
onChange={handleChange}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
id="project"
name="project"
label="Personal Projects"
fullWidth
variant="standard"
value={professionalInfo.project || ""}
onChange={handleChange}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
id="linkedin"
name="linkedin"
label="Linkedin url"
fullWidth
variant="standard"
value={professionalInfo.linkedin || ""}
onChange={handleChange}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
id="accomp"
name="accomp"
label="write a paragraph on Your Accomplishments"
fullWidth
variant="standard"
value={professionalInfo.accomp || ""}
onChange={handleChange}
/>
</Grid>
</Grid>
<Button onClick={handleSubmit}>Submit</Button>
<Button onClick={handleReset}>Reset</Button>
</>
);
};
export default ProfessionalInfoUI;
professionalInfo.js:
// ProfessionalInfo.js
import React from "react";
import ProfessionalInfoUI from "./ProfessionalInfoUI";
import useProfessionalInfoForm from "./useProfessionalInfoForm";
const ProfessionalInfo = () => {
const {
professionalInfo,
handleChange,
handleWorkChange,
handleWorkDateChange,
handleAddWorkExperience,
handleRemoveWorkExperience,
handleSubmit,
handleReset,
} = useProfessionalInfoForm();
return (
<ProfessionalInfoUI
professionalInfo={professionalInfo}
handleChange={handleChange}
handleWorkChange={handleWorkChange}
handleWorkDateChange={handleWorkDateChange}
handleAddWorkExperience={handleAddWorkExperience}
handleRemoveWorkExperience={handleRemoveWorkExperience}
handleSubmit={handleSubmit}
handleReset={handleReset}
/>
);
};
export default ProfessionalInfo;
Also I have wrote all my states in zustand: here is the code for that:
import create from "zustand";
import { devtools } from "zustand/middleware";
import { persist } from "zustand/middleware";
// hook to manage zustand store. We are storing initial state in store and updating it when setPersonalInfo and resetPersonalInfo hooks are being called
const useProfessionalInfoStore = create(
persist(
devtools((set) => ({
professionalInfo: {
degree: "",
workExperiences: [],
cert: "",
skill: "",
project: "",
linkedin: "",
accomp: "",
},
setProfessionalInfo: (data, callback) => {
set((state) => ({
professionalInfo: { ...state.professionalInfo, ...data },
}));
if (callback && typeof callback === "function") {
callback();
}
},
resetProfessionalInfo: () => {
set(() => ({
professionalInfo: {
degree: "",
workExperiences: [],
cert: "",
skill: "",
project: "",
linkedin: "",
accomp: "",
},
}));
},
})),
{
name: "professionalInfo-store", // Unique name for your store's persisted data
getStorage: () => localStorage, // Choose your storage mechanism
}
)
);
export default useProfessionalInfoStore;
The problem is that When I extracted my business logic from UI. Nothing is working. Can someone please tell me what is the problem.
The issue might be related to how you are connecting the form logic with the UI component. There might be a problem with the way you are passing and using the props in your ProfessionalInfoUI component.
Additionally, it's a good idea to log the values and functions at different points in your components to debug and check if the values are being passed correctly. For example, add console.log statements in both the hook and the UI component to see if the values are as expected.
I hope this is helpfull.