I am trying to create a web app that generates a resume as the user types information.
I am having trouble changing, via input/handleChange, the the properties of the objects in my component's state. I know I can change the state object properties by using the input's name as a reference to the property I am trying to change, but I do not know how to do this when there are multiple objects in the component's state.
I have a button that adds an object to the schoolData state and adds the component to the schoolArray state.
Input component:
import React from 'react';
import Schools from './Schools';
export default function Input(props) {
// school data and event handler
const [schoolData, setSchoolData] = React.useState(
[{
schoolName: '', schoolState: '', schoolCity: '', schoolDegree: '', schoolStartDate: '', schoolEndDate: '', schoolCurrent: false,
}]
);
function handleSchoolChange(event) {
const { name, value, type, checked } = event.target;
setSchoolData(prevData => ({
...prevData,
[name]: type === "checkbox" ? checked : value
}));
console.log('school data', schoolData);
console.log('school data name', schoolData.name);
console.log('school array', schoolArray);
}
// school array and event handler
const [schoolArray, setSchoolArray] = React.useState([
<Schools key={schoolData.length} schoolData={schoolData} handleSchoolChange={handleSchoolChange} />,
]);
function addSchool() {
// add schoolData object
setSchoolData(prevData => [...prevData, {
schoolName: '', schoolState: '', schoolCity: '', schoolDegree: '', schoolStartDate: '', schoolEndDate: '', schoolCurrent: false,
}]);
setSchoolArray(prevSchoolArray => [...prevSchoolArray, <Schools key={schoolData.length 1} schoolData={schoolData[0]} handleSchoolChange={handleSchoolChange} />]);
console.log(schoolArray);
console.log(schoolElements);
console.log('school name', schoolData[0].schoolName);
}
const schoolElements = schoolArray.map((school, index) =>
{ return school }
);
return (
<form>
<legend>Education</legend>
{schoolElements}
</form>
)
}
Schools component:
export default function Schools(props) {
return (
<div>
<label htmlFor="schoolName">School Name</label>
<input
type="text"
placeholder="School Name"
className="input"
name="schoolName"
onChange={props.handleSchoolChange}
value={props.schoolData.schoolName}
/>
<label htmlFor="schoolCity">City</label>
<input
type="text"
placeholder="City"
className="input"
name="schoolCity"
onChange={props.handleSchoolChange}
value={props.schoolData.schoolCity}
/>
<label htmlFor="schoolState">State</label>
<input
type="text"
placeholder="State"
className="input"
name="schoolState"
onChange={props.handleSchoolChange}
value={props.schoolData.schoolState}
/>
<label htmlFor="schoolDegree">Degree</label>
<input
type="text"
placeholder="Degree"
className="input"
name="schoolDegree"
onChange={props.handleSchoolChange}
value={props.schoolData.schoolDegree}
/>
<label htmlFor="schoolStartDate">Start Date</label>
<input
type="month"
placeholder="Start Date"
className="input"
name="schoolStartDate"
onChange={props.handleSchoolChange}
value={props.schoolData.schoolStartDate}
/>
<label htmlFor="schoolEndDate">End Date</label>
<input
type="month"
placeholder="End Date"
className="input"
name="schoolEndDate"
onChange={props.handleSchoolChange}
value={props.schoolData.schoolEndDate}
/>
<label htmlFor="schoolCurrent">Still attending</label>
<input
type="checkbox"
name="schoolCurrent"
onChange={props.handleSchoolChange}
checked={props.schoolData.schoolCurrent}
/>
</div>
);
}
Thanks in advance for any help!
CodePudding user response:
The easier solution would be to keep your schoolData state variable updated through the input onchanges. This should keep your schoolData in your Input component up to date to what you want.
Schools component
export default function Schools(props) {
return (
<div>
<label htmlFor="schoolName">School Name</label>
<input
type="text"
placeholder="School Name"
className="input"
name="schoolName"
onChange={(e) => props.setSchoolData({ ...props.schoolData, schoolName: e.target.value })}
value={props.schoolData.schoolName}
/>
<label htmlFor="schoolCity">City</label>
<input
type="text"
placeholder="City"
className="input"
name="schoolCity"
onChange={(e) => props.setSchoolData({ ...props.schoolData, schoolCity: e.target.value })}
value={props.schoolData.schoolCity}
/>
<label htmlFor="schoolState">State</label>
<input
type="text"
placeholder="State"
className="input"
name="schoolState"
onChange={(e) => props.setSchoolData({ ...props.schoolData, schoolState: e.target.value })}
value={props.schoolData.schoolState}
/>
<label htmlFor="schoolDegree">Degree</label>
<input
type="text"
placeholder="Degree"
className="input"
name="schoolDegree"
onChange={(e) => props.setSchoolData({ ...props.schoolData, schoolDegree: e.target.value })}
value={props.schoolData.schoolDegree}
/>
<label htmlFor="schoolStartDate">Start Date</label>
<input
type="month"
placeholder="Start Date"
className="input"
name="schoolStartDate"
onChange={(e) => props.setSchoolData({ ...props.schoolData, schoolStartDate: e.target.value })}
value={props.schoolData.schoolStartDate}
/>
<label htmlFor="schoolEndDate">End Date</label>
<input
type="month"
placeholder="End Date"
className="input"
name="schoolEndDate"
onChange={(e) => props.setSchoolData({ ...props.schoolData, schoolEndDate: e.target.value })}
value={props.schoolData.schoolEndDate}
/>
<label htmlFor="schoolCurrent">Still attending</label>
<input
type="checkbox"
name="schoolCurrent"
onChange={(e) => props.setSchoolData({ ...props.schoolData, schoolCurrent: e.target.value })}
checked={props.schoolData.schoolCurrent}
/>
</div>
);
}
CodePudding user response:
Theres quite a lot wrong here. You shouldnt store components in state, you only need the data. This is greatly confusing matters.
What you need to do instead is iterate over schoolData
in the render method, and pass down appropriate bindings for the data itself for the individual school, and a callback that handles changes for that school.
import React from 'react';
import Schools from './Schools';
export default function Input(props) {
// school data and event handler
const [schoolData, setSchoolData] = React.useState(
[{
schoolName: '', schoolState: '', schoolCity: '', schoolDegree: '', schoolStartDate: '', schoolEndDate: '', schoolCurrent: false,
}]
);
function handleSchoolChange(event, indexToChange) {
const { name, value, type, checked } = event.target;
setSchoolData(prevData => {
return prevData.map((school, index) => index === indexToChange ? {
...prevData[index],
[name]: type === "checkbox" ? checked : value
}) : school
});
}
function addSchool() {
// add schoolData object
setSchoolData(prevData => [...prevData, {
schoolName: '', schoolState: '', schoolCity: '', schoolDegree: '', schoolStartDate: '', schoolEndDate: '', schoolCurrent: false,
}]);
}
return (
<form>
<legend>Education</legend>
{schoolData.map((school, index) => (<School key={index} schoolData={school} handleSchoolChange={(e) => handleSchoolChange(e, index)} />)}
</form>
)
}
CodePudding user response:
One thing I can suggest is in your Input component, have a single object variable and a object array variable, fill information using the single object, when info is filled add it to the object array, and then pass the object array as a prop into School. Then in the School component you can return the array mapped or something else if array is empty.
function Input(props) {
const [schoolData, setSchoolData] = useState({ single object })
const [schoolsArray, setSchoolsArray] = useState([{},...])
// use setSchoolData to fill out schoolData Variable
const handleAddingSchool = () => {
setSchoolsArray([...schoolsArray, schoolData])
}
return (
<>
<School schoolArray={schoolsArray}/>
<>
)
}
// New Basic School Component Layout
function School (props) {
return {
<>
{
props.schoolArray.length > 0
? <SchoolComponentStuff....>
: <NothingToSeeHere....>
}
<>
}
}
You will have to fill in your key pieces but I think this will resolve your issue.