Home > Software engineering >  How to get around React setState Delay?
How to get around React setState Delay?

Time:03-27

I have been stuck on the simple issue of the common React setState delay. I am currently looking to update an object within an array, by saving it to a state variable "newStud" within a child component, and pass it into a parent component to be utilized for a filtering function. My current issue is that state only updates completely after the second submission of an entry on my site. Thus, when the filter function in the parent component aims to read the array being passed in, it throws errors as the initial declaration of state is what is passed in. My question is if there is some way I can adjust for that delay in updating that information without having to break apart my larger components into smaller more manageable components?

For reference, here is the code I am utilizing for the child component (the issue is present in my "addTag" function):

import React, {useState, useEffect} from 'react';
import './studentcard.css';

import { Tags } from '../Tags/tags.js';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPlus } from '@fortawesome/free-solid-svg-icons';
import { faMinus } from '@fortawesome/free-solid-svg-icons';


export function StudentCard({student, upStuds}) {
  const [newStud, setNewStud] = useState({});
  const [clicked, setClicked] = useState(false);
  const [tag, setTag] = useState('');

  // switches boolean to opposite value for plus/minus icon display
  const onClick = () => {
    setClicked(!clicked);
  };

  // triggers the addTag function to push a tag to the array within the student object
  const onSubmit = async (e) => {
    e.preventDefault();
    
    await addTag(tag);
  };

  // captures the values being entered into the input 
  const onChange = (e) => {
    setTag(e.target.value);
  };

  // this pushes the tag state value into the array that is located in the student object being passed down from the parent component
  // it is meant to save the new copy of the "student" value in "newStuds" state variable, and pass that into the callback func
  // ********** here is where I am experiencing my delay ************
  const addTag = () => {

    student.tags.push(tag);
    setNewStud({...student});
    upStuds(newStud);

    setTag('');
  };

  let scores;
  if (clicked !== false) {
   scores = <ul className='grades-list'>
      {student.grades.map((grade, index) => <li key={index} className='grade'>Test {(index   1)   ':'}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{grade}%</li>)}
    </ul>;
  } 

  return (
    <div className='studentCard' >
      <div className='pic-and-text'>
        <img className='student-image' alt='' src={student.pic}></img>
        <section className='right-side'>
            <h3 id='names'>{student.firstName.toUpperCase()   ' '   student.lastName.toUpperCase()}</h3>
            <h4 className='indent'>Email: {student.email}</h4>
            <h4 className='indent'>Company: {student.company}</h4>
            <h4 className='indent'>Skill: {student.skill}</h4>
            <h4 className='indent'>Average: {student.grades.reduce((a, b) => parseInt(a)   parseInt(b), 0) / student.grades.length}%</h4>
            {scores}
            <Tags student={student}/>
            <form className='tag-form' onSubmit={onSubmit}>
              <input className='tag-input' type='text' placeholder='Add a tag' onChange={onChange} value={tag}></input>
            </form>
        </section>
      </div>  
         <FontAwesomeIcon icon={clicked !== false ? faMinus : faPlus} className='icon' onClick={onClick}/>
    </div>
  )
};

And if necessary, here is the Parent Component which is attempting to receive the updated information (the callback function I am using to fetch the information from the child component is called "upStuds") :

import React, {useState, useEffect} from 'react';
import './dashboard.css';

import {StudentCard} from '../StudentCard/studentcard';

import axios from 'axios';

export function Dashboard() {
    const [students, setStudents] = useState([]);
    const [search, setSearch] = useState('');
    const [tagSearch, setTagSearch] = useState('');

    useEffect(() => {
      const options = {
        method: 'GET',
        url: 'https://api.hatchways.io/assessment/students'
      };

      var index = 0;
      function genID() {
        const result = index;
        index  = 1;

        return result;
      };

      axios.request(options).then((res) => {
        const students = res.data.students;
        const newData = students.map((data) => {
          const temp = data;

          temp["tags"] = [];
          temp["id"] = genID();
          return temp;
        });

        setStudents(newData);
      }).catch((err) => {
        console.log(err);
      });
    }, []);

    const onSearchChange = (e) => {
      setSearch(e.target.value);
    };

    const onTagChange = (e) => {
      setTagSearch(e.target.value);
    }; 
    
    // here is the callback function that is not receiving the necessary information on time
    const upStuds = (update) => {
      let updatedCopy =  students;

      updatedCopy.splice(update.id, 1, update);

      setStudents(updatedCopy);

    };

    // const filteredTagged = tagList.filter
  return (
    <div className='dashboard'>
      <input className='form-text1' type='text' placeholder='Search by name' onChange={onSearchChange}></input>
      <input className='form-text2' type='text' placeholder='Search by tag' onChange={onTagChange}></input>

      
      {students.filter((entry) => {
        const fullName = entry.firstName   entry.lastName;
        const fullNameWSpace = entry.firstName   ' '   entry.lastName;
        if (search === '') {
          return entry;
        } else if (entry.firstName.toLowerCase().includes(search.toLowerCase()) || entry.lastName.toLowerCase().includes(search.toLowerCase()) 
        || fullName.toLowerCase().includes(search.toLowerCase()) || fullNameWSpace.toLowerCase().includes(search.toLowerCase())) {
          return entry;
        }
      }).map((entry, index) => {
        return (<StudentCard student={entry} key={index} upStuds={upStuds} />)
      })}
    </div>
  )
};

Please let me know if I need to clarify anything! Thanks for any assistance!

CodePudding user response:

setNewStud({...student});
upStuds(newStud);

If you want to send the new state to upStuds, you can assign it to a variable and use it twice:

const newState = {...student};
setNewStud(newState);
upStuds(newState);

Additionally, you will need to change your upStuds function. It is currently mutating the existing students array, and so no render will occur when you setStudents. You need to copy the array and edit the copy.

const upStuds = (update) => {
  let updatedCopy = [...students]; // <--- using spread operator to create a shallow copy
  updatedCopy.splice(update.id, 1, update);
  setStudents(updatedCopy);
}
  • Related