ghophri
ghophri

Reputation: 221

React Hooks state not updating variable in view

I'm trying to set a state, which I fetch from an API in the form of an array.

Tried this in every way possible, doesn't work.

Any ideas on how to fix this?

instance is an axios.create that creates the instance to a localhost django server which has CORS-ALLOW-CROSS-ORIGIN True

import React, { useState } from "react";
import { instance } from "../../stores/instance";

const OneList = () => {
  const [one, setOne] = useState([]);
  const fetchText = async () => {
    const response = await instance.get(`/one/list/`);
    setOne(response.data);
  };
  fetchText();
  return (
    <>
      <div>Hello World.</div>
      {one.forEach(o => (
        <p>o.text</p>
      ))}
    </>
  );
};
export default OneList;

Upvotes: 1

Views: 545

Answers (3)

ghophri
ghophri

Reputation: 221

Based on you guys' advice, I came up with the below code which works fine so far.

import React, { useState, useEffect } from "react";
import { instance } from "../../stores/instance";

const OneList = () => {
  const [one, setOne] = useState([]);
  const [el, setEl] = useState(null);

  const fetchText = async () => {
    let res = await instance.get("/one/list/");
    setOne(res.data);
  };

  useEffect(() => {
    (async () => {
      await fetchText();
    })();
  }, []);
  useEffect(() => {
    const handleClick = e => {
      let newEl = document.createElement("input");
      newEl.value = e.target.innerHTML;
      newEl.id = e.target.id;
      newEl.addEventListener("keypress", e => handleInput(e));
      newEl.addEventListener("focusout", e => handleInput(e));
      e.target.parentNode.replaceChild(newEl, e.target);
      newEl.focus();
    };
    const handleInput = async e => {
      console.log(e.type);
      if (
        e.target.value !== "" &&
        (e.key === "Enter" || e.type === "focusout")
      ) {
        let payload = { text: e.target.value };
        try {
          e.preventDefault();
          let res = await instance.put(`/one/update/${e.target.id}`, payload);
          let text = res.data.text;
          e.target.value = text;
          let newEl = document.createElement("span");
          newEl.innerHTML = text;
          newEl.addEventListener("click", e => handleClick(e));
          newEl.id = e.target.id;
          e.target.parentNode.replaceChild(newEl, e.target);
        } catch (e) {
          console.error(e);
        }
      }
    };

    setEl(
      one.map(o => (
        <p>
          <span id={o.id} onClick={e => handleClick(e)}>
            {o.text}
          </span>
        </p>
      ))
    );
  }, [one]);

  return <>{el}</>;
};

export default OneList;

Upvotes: 0

SteveJerbs
SteveJerbs

Reputation: 31

This looks to be a good use case for a useEffect hook. Also, you need to await async functions within useEffect statement. The useEffect hook cannot be an async function in itself. Also, the original implementation would result in an infinite loop. The setState function would trigger a re-render which would then trigger the fetch function to fire off again. Consider the following:

import React, { useState, useEffect } from "react";
import { instance } from "../../stores/instance";

const OneList = () => {
  const [one, setOne] = useState([]);

  const fetchText = async () => {
    const request= await instance.get(`/one/list/`);
    request.then( r => setOne(r.data))
  };

  useEffect(() => {
    (async () => {
      await fetchText();
    })();
  }, []);

  return (
    <>
      <div>Hello World.</div>
      {one.forEach(o => (
        <p>o.text</p>
      ))}
    </>
  );
};
export default OneList;

Upvotes: 1

Zunaib Imtiaz
Zunaib Imtiaz

Reputation: 3119

Do it like this,

import React, { useState } from "react";
import { instance } from "../../stores/instance";

const OneList = () => {
  const [one, setOne] = useState([]);
  const fetchText = async () => {
    const response = await instance.get(`/one/list/`);
    setOne(response.data);
  };

  useEffect(() => {
    fetchText();
  },[ any variable you want it to fetch that again ]);



  return (
    <>
      <div>Hello World.</div>
      {one.forEach(o => (
        <p>o.text</p>
      ))}
    </>
  );
};
export default OneList;

Upvotes: 1

Related Questions