Home > Net >  Local state gets reset after adding a new item in React
Local state gets reset after adding a new item in React

Time:10-27

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>
  );
};

Edit local-state-gets-reset-after-adding-a-new-item-in-react

  • Related