I am making a small React app to keep track of homework assignments. You should be able to add multiple dates, and within those dates add multiple assignments. I am able to print the dates to the screen as well as print out multiple assignments for a single day. However after adding another date, the local state that stores the assignments in all of the previous dates gets wiped out so the assignments for each date disappears. All of the dates still show on the screen but the assignments within them are no longer there and no longer in state. How can I get the assignments to remain on the screen and in state when adding a new date? Is this something I would need local storage for? I would appreciate any help I can get. Below is my code:
import Button from './Button';
import DatesInput from './DatesInput';
import DatesDisplay from './DatesDisplay';
const AssignmentCalendar = () => {
const [dates, setDates] = useState([]);
const addDate = (date) => {
setDates(dates.concat(date))
}
return (
<main className="main-display">
<h3>Assignment Calendar</h3>
<h4 className="dates-Title">Dates</h4>
<Button
text='Add Date'
color='green'
/>
<DatesInput dates={dates} addDate={addDate}/>
<DatesDisplay dates={dates} />
</main>
)
}
export default AssignmentCalendar
import React, { useState } from 'react'
import Button from './Button'
const DatesInput = ({ addDate }) => {
const [ date, setDate ] = useState('')
const typeInDate = (e) => {
setDate(e.target.value)
}
const submitDate = (e) => {
e.preventDefault();
addDate(date)
setDate('')
}
return (
<form className='dates-input' onSubmit={submitDate}>
<label className='input-label'>Please enter a new date...</label>
<input type='text' value={date} onChange={typeInDate} ></input>
<Button text='Submit' />
</form>
)
}
export default DatesInput
import React from 'react'
import Date from './Date';
import { v4 as uuidv4 } from 'uuid';
const DatesDisplay = ({ dates }) => {
return (
<div className='assignment-display'>
{dates.map((date) => {
return (
<div key={uuidv4()}>
<Date date={date}/>
</div>
)
})}
</div>
)
}
export default DatesDisplay
import React, { useState } from 'react';
import Assignments from './Assignments';
import Button from './Button';
const Date = ({ date }) => {
const [assignments, setAssignments] = useState([]);
const addAssignment = (assignment) => {
setAssignments(assignments.concat(assignment))
}
return (
<div>
<h5>{date} <Button text='Add Assignment'/></h5>
<div className='assignment-display'>
<Assignments assignments={assignments} addAssignment={addAssignment} />
</div>
</div>
)
}
export default Date
import React from 'react'
import AssignmentInput from './AssignmentInput';
import AssignmentDisplay from './AssignmentDisplay';
const Assignments = ({ assignments, addAssignment }) => {
return (
<div>
<AssignmentInput addAssignment={addAssignment} />
<AssignmentDisplay assignments={assignments} />
</div>
)
}
export default Assignments
import React, { useState } from 'react'
import Button from './Button';
const AssignmentInput = ({ addAssignment }) => {
const [assignment, setAssignment] = useState({
name: '',
subject: '',
});
const handleAddName = (e) => {
setAssignment({
name: e.target.value,
subject: assignment.subject,
})
}
const handleAddsubject = (e) => {
setAssignment({
name: assignment.name,
subject: e.target.value,
})
}
const submitNewAssignment = (e) => {
e.preventDefault();
addAssignment(assignment);
setAssignment({
name: '',
subject: '',
});
}
return (
<form onSubmit={submitNewAssignment}>
<label>Please enter a new assignment...</label>
<input type='text' placeholder='Assignment name' value={assignment.name} onChange={handleAddName}></input>
<input type='text' placeholder='Subject' value={assignment.subject} onChange={handleAddsubject}></input>
<Button text='Submit' />
</form>
)
}
export default AssignmentInput
import React from 'react'
import { v4 as uuidv4 } from 'uuid';
import Assignment from './Assignment';
const AssignmentDisplay = ({ assignments }) => {
return (
<div>
{assignments.map((assignment) => {
return (
<div key={uuidv4()}>
<Assignment name={assignment.name} subject={assignment.subject}/>
</div>
)
})}
</div>
)
}
export default AssignmentDisplay
import React from 'react'
import { FaTimes, FaCheckCircle } from 'react-icons/fa'
const Assignment = ({ name, subject }) => {
return (
<div>
<FaCheckCircle style={{color: 'green', cursor: 'pointer'}} />
<h6 className='assignment-names'>
{name}
<FaTimes
style={{color: 'red', cursor: 'pointer'}}
/>
</h6>
<p className='assignment-date'>
{subject}
</p>
</div>
)
}
export default Assignment
CodePudding user response:
Issue
You are essentially using a random React key for each Date
component being mapped in DateDisplay
when you generate a new GUID for each element.
const DatesDisplay = ({ dates }) => {
return (
<div className='assignment-display'>
{dates.map((date) => {
return (
<div key={uuidv4()}> // <-- new unique key each render
<Date date={date}/>
</div>
)
})}
</div>
)
}
If the React keys for each element are not stable between renders, then React interprets this as a new component and unmounts the previous "instance" and mounts a new "instance", thus throwing away any component state.
Solution
An ideal solution would likely have the array of assignments stored in the dates
state array in AssignmentCalendar
component, but to specifically address this issue you just need to provide stable React keys for each date
element in the dates
state. For this I suggest moving the GUID generation into AssignmentCalendar
component when a new date is added to state.
const AssignmentCalendar = () => {
const [dates, setDates] = useState([]);
const addDate = (date) => {
setDates(dates.concat({
id: uuidv4(), // <-- generate GUID id property
date,
}));
};
return (
<main className="main-display">
<h3>Assignment Calendar</h3>
<h4 className="dates-Title">Dates</h4>
<button type="button">Add Date</button>
<DatesInput dates={dates} addDate={addDate} />
<DatesDisplay dates={dates} />
</main>
);
};
Destructure the date
and id
in DatesDisplay
when mapping, using the id
as the React key still.
const DatesDisplay = ({ dates }) => {
return (
<div className="assignment-display">
{dates.map(({ date, id }) => (
<div key={id}> // <-- now a stable id
<Date date={date} />
</div>
))}
</div>
);
};