skip to Main Content

I’m currently learning ReactJS, so if I miss any fundamentals please let me know.

I’ve written a frontend using React that should render out some markdown text it gets from an API, however when running the fetch request it runs multiple times causing the data populated from the original fetch to be changed/removed

console output from fetch

how do I stop the fetch from pulling empty data after it’s populated?

import React, { useState, useEffect, useRef } from "react";
import { useParams, useNavigate } from "react-router-dom";
import DocumentLayout from "../components/DocumentLayout";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";

const EditDocument = () => {
  const params = useParams();

  let navigate = useNavigate;

  let [loading, setLoading] = useState(true);
  let [document, setDocument] = useState({});

  useEffect(() => {
    const fetchDocument = async () => {
      const res = (
        await fetch(`http://127.0.0.1:8080/markdown?id=${params.id}`)
      )
        .json()
        .then((res) => setDocument(res));

      if (res.status === 404) {
        navigate("/");
      }
      setLoading(false);
    };
    fetchDocument();
  }, [params.id]);

  console.log(document);

  return loading ? (
    <h3>Loading...</h3>
  ) : (
    <DocumentLayout>
      <ReactQuill
        theme="snow"
        value={document?.markdown}
        onChange={(document) => setDocument(document)}
      />
      <button className="editor save-icon">Save</button>
    </DocumentLayout>
  );
};

export default EditDocument;

I’ve looked at similar problems on Stack Overflow, and googled the issue, but most seem to address the async part of the fetch and not the multiple requests.

I’m expecting to have one request and return one set of data.

2

Answers


  1. Here you should add checking block of params.id.
    You could get undefined for that, and you need to fetch when it is number.

    const fetchDocument = async () => {
      const res = (
        await fetch(`http://127.0.0.1:8080/markdown?id=${params.id}`)
      )
        .json()
        .then((res) => setDocument(res));
    
      if (res.status === 404) {
        navigate("/");
      }
      setLoading(false);
    };
    
    useEffect(() => {
      if (typeof params.id !== "undefined") fetchDocument();
    }, [params.id]);
    
    Login or Signup to reply.
  2. There’s a couple things to point out

    1. mixing async..await with .then is cumbersome
    2. you are fetching data without cleaning up and properly handling side effects
      useEffect(() => {
        const fetchDocument = async () => { //
          const res = (
            await fetch(`http://127.0.0.1:8080/markdown?id=${params.id}`)
          )
            .json() // ❌ mix async..await with then
            // ❌ possible setState on unmounted component
            .then((res) => setDocument(res));
    
          if (res.status === 404) {
            navigate("/"); // ❌ no error communication
          }
          setLoading(false); // ❌ possible setState on unmounted component
        };
        fetchDocument();
        // ❌ no clean up
      }, [params.id]);
    

    Here’s how I would go about fixing it –

    // ✅ isolated, reusable api 
    async function getMarkdown(id) {
      const res = await fetch(`http://127.0.0.1:8080/markdown?id=${id}`)
      if (res.ok) return res.json()
      throw Error(`failed to get markdown: HTTP ${res.status}`)
    }
    

    Now your component’s effect can focus solely on the parts it is responsible for. See the fetching data guide from the React docs for more guidance –

    useEffect(
      () => {
        let mounted = true // ✅ local mounted state
    
        // ✅ no need to define a new function
        // ✅ only execute side effects like setState or navigate if mounted
        getMarkdown(params.id)
          .then(doc => mounted && setDocument(doc))
          .catch(err => mounted && navigate(`/?error=could-not-fetch`))
          .finally(() => mounted && setLoading(false))
    
        return () => {
          mounted = false // ✅ component unmounted
        }
      },
      [params.id]
    )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search