Reputation: 221
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
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
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
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