Home > Blockchain >  Need to Update State Synchronously after Fetch API Request
Need to Update State Synchronously after Fetch API Request

Time:04-12

I have a search component that fetches a single profile from a JSON file (currently local, but will be remote in the future) and displays the information of the matching profile beneath the input field.

Currently, on my first submit of my search query, I've found that all of my state variables return undefined because, if I understand correctly, state does not update until the full chain of promises has resolved. And it's only on my second submit of my search query that my state variables return the correct data of the filtered search result.

On the first submit, it appears that an empty array is being initialized, as my conditional render of {props.activeChart && `OPENED CHART : ${props.firstName} ${props.lastName} (DOB: ${props.DOB})`} becomes truthy and renders out empty values for firstName, lastName, and DOB.

EDIT: I came across this recent post (React state gets updated only after I submit the form twice), which seems to address this same issue resulting from asynchronous fetch and setting state, except with axios. I've tried modifying my code accordingly (edited below), but I'm still not able to update state after my fetch result has resolved. Any advice would be appreciated. Thanks.

import { useState } from 'react';
import StyledSearchForm from './SearchForm.styled';

const SearchForm = props => {
  const [queryFirstName, setQueryFirstName] = useState('');
  const [queryLastName, setQueryLastName] = useState('');
  const [queryDOB, setQueryDOB] = useState('');

  const handleQuery = async (e) => {
    e.preventDefault();

    const result = await fetchRecord();

    console.log(result[0]) // fetched object successfully logged

    if (result[0]) {
      setActiveChart(result[0]);

      console.log(activeChart) // activeChart still undefined

      setFirstName(activeChart.firstName);
      setLastName(activeChart.lastName);
      setDOB(activeChart.dob);
    }
  };

  const fetchRecord = () => (
    fetch('http://localhost:8000/patients')
    .then(resp => { return resp.json(); })
    .then(data => {
      const result = data.filter(patient => (
        (patient.dob === queryDOB.trim() &&
          patient.lastName.toLowerCase() === 
          queryLastName.toLowerCase().trim()) || 
        (patient.lastName.toLowerCase() === 
          queryLastName.toLowerCase().trim() &&
          patient.firstName.toLowerCase() === 
          queryFirstName.toLowerCase().trim())
      ));
      return {...result};
    })
  );

  return (
    <StyledSearchForm>
      <form onSubmit={handleQuery}>
        <label className="first-name" htmlFor="first-name">
          First Name:
        </label>
        <input
          type="text"
          id="first-name"
          className="form-fields"
          name="fname"
          value={queryFirstName} 
          onChange={e => setQueryFirstName(e.target.value)}
        />
        <label className="last-name" htmlFor="last-name">
          Last Name:
        </label>
        <input
          type="text"
          id="last-name"
          className="form-fields"
          name="lname"
          value={queryLastName}
          onChange={e => setQueryLastName(e.target.value)}
        />
        <label className="dob" htmlFor="dob">
          DOB:
        </label>
        <input
          type="text"
          id="dob"
          className="form-fields"
          name="dob"
          value={queryDOB}
          onChange={e => setQueryDOB(e.target.value)}
        />
        <button className="submit-btn" type="submit" onClick={e => handleQuery}>Open Chart</button>
      </form>

      <div className="active-patient">
        {props.activeChart && `OPENED CHART : ${props.firstName} ${props.lastName} (DOB: ${props.DOB})`}
      </div>
    </StyledSearchForm>
  );
};

export default SearchForm;

CodePudding user response:

It looks like you're expecting your data filter to return an object, but Array.prototype.filter (docs) returns an array. Arrays, even if empty, are truthy.

You need to handle an array, not an object, in this chain:

  const fetchRecord = () =>
    fetch("http://localhost:8000/patients")
      .then((resp) => {
        return resp.json();
      })
      .then((data) => {
        // results is an array!
        const results = data.filter(...);

        if (results.length === 0) {
          // no match - do something about it?
          return {};
        } else {
          // return the first result?
          return results[0];
        }
      })
      .then((record) => {
        props.setActiveChart(...record);
      })
      .then(() => {
        props.setFirstName(props.activeChart.firstName);
        props.setLastName(props.activeChart.lastName);
        props.setDOB(props.activeChart.dob);
      });

CodePudding user response:

It seems the issue resulted from trying to set all of my state variables in the same async function that was fetching my search result, and by moving the if(results[0]) statement out of the handleQuery function while leaving just the setActiveChart() inside the handleQuery function resolved my issue.

  • Related