Why does the POST stop working when setting state in useEffect? - javascript

Why does the POST stop working when trying to set State using useEffect and fetch?
In this first example, I can see in the POST log in the Chrome console and print in Flask:
React:
import React, { useEffect, useState } from "react";
import Button from '#mui/material/Button';
import ArrowUpwardIcon from '#mui/icons-material/ArrowUpward';
import TextField from '#mui/material/TextField';
export default function Heater(props) {
const [status, setStatus] = useState({
"id": props.id,
"name": props.name,
"pin": props.pin,
"setpoint": 1
});
function incrementSetpoint() {
setStatus(previousValue => {
return {
...previousValue,
"setpoint": status.setpoint + 1
};
});
};
useEffect(() => {
fetch('/heaters', {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify(status)
})
.then((response) => response.json())
.then((response) => console.log(response))
.catch((error) => (console.error(error)))
});
return (
<div>
<h1>{ props.name }</h1>
<div className="inputs-container" style={{ marginBottom: '2ch' }}>
<TextField label="Setpoint" value={ status.setpoint } />
<Button variant="contained" onClick={ incrementSetpoint }>
<ArrowUpwardIcon></ArrowUpwardIcon>
</Button>
</div>
</div>
);
}
Flask:
from flask import jsonify
#app.route('/heaters', methods=['GET', 'POST'])
def heaters():
"""Control heaters and return the current heater configuration."""
if request.method == 'POST':
print(request.json)
return jsonify(request.json)
Now, if I comment out the console.log and use setState instead, it causes and infinite loop in Flask.
...
useEffect(() => {
fetch('/heaters', {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify(status)
})
.then((response) => response.json())
// .then((response) => console.log(response))
.then((response) => setStatus(response)) // New line here
.catch((error) => (console.error(error)))
});
...
After reading some other posts, I decided to add an empty array to useEffect. This stops the infinite loop. I can see that setStatus is working by adding console.log(status). However, the POST is no longer working.
...
useEffect(() => {
fetch('/heaters', {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify(status)
})
.then((response) => response.json())
// .then((response) => console.log(response))
.then((response) => setStatus(response)) // New line here
.catch((error) => (console.error(error)))
}, []); // Empty array added here
console.log(status); // New line here to confirm setStatus works
...
On the initial startup, I can see the POST log in the Chrome console and print in Flask. After it has loaded, the POST does not work. Any ideas on what is causing the issue?

When you use useEffect() bound to an empty array, it only executes once on the first run.
useEffect(() => {
// fetch code here
}, []);
I'm guessing that you want to run it again on every status update?
If that's the case, bind useEffect() to your status state.
useEffect(() => {
// fetch code here
}, [status]);
Thing is, if you setStatus in that same useEffect() hook, you end up in the infinite loop - because it runs again after the status is updated.. and then again.. and again.
So you want to create a different state heaters [heaters, setHeaters] then setHeaters after you fetch, rather than setStatus again.
const [status, setStatus] = useState({
"id": props.id,
"name": props.name,
"pin": props.pin,
"setpoint": 1
});
const [heaters, setHeaters] = useState({});
useEffect(() => {
fetch('/heaters', {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify(status)
})
.then((response) => response.json())
.then((response) => setHeaters(response))
.catch((error) => (console.error(error)))
}, [status]);
That way, whenever you incrementSetpoint() the status will update, the fetch will run, and heaters is set.
Finally, you see your fetched data by using another useEffect() bound to the heaters state.
useEffect(() => {
console.log(heaters);
}, [heaters]);
This should be the convention.

Related

How to update the state after a data update through an API request in React?

I have a small problem. I have this fetch data in one component which is working perfectly:
const [item, setItem] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetch("https://api/products")
.then((response) => response.json())
.then((data) => setItem(data))
.finally(() => setLoading(false));
}, []);
Then I have this POST in a different component:
const handleSubmit = async (e) => {
e.preventDefault();
try {
await fetch("https://api/products", {
method: "POST",
headers: { admin: "true", "Content-Type": "application/json" },
body: JSON.stringify(body),
});
} catch (error) {
console.log("error");
}
};
How I can get the new data of the first component after using POST without reloading the web page?
You need to be able to append that new data, body inside item array, means you need to call this setItem(prev => [...prev, body]) in your try-catch, something like:
const handleSubmit = async (e) => {
e.preventDefault();
try {
await fetch("https://api/products", {
method: "POST",
headers: { admin: "true", "Content-Type": "application/json" },
body: JSON.stringify(body),
});
setItem(prev => [...prev, body]); // here you add the new data
} catch (error) {
console.log("error");
}
};
Now how you would access this setItem function? Well if the component that's making the POST request is a child of the component where you have this line const [item, setItem] = useState([]), you could pass setItem as a props.
If it's not the case, either you move this const [item, setItem] = useState([]) inside a component that's parent of all those components and pass down what you need as props or use a state management solution like the context API.
You must call the function again when response is true.
I mean your function when getting data.

axios get run constantly when call

I create app.get to return token from Spotify
here is the code :
axios({
url: "https://accounts.spotify.com/api/token",
method: "POST",
headers: {
Authorization:
"Basic " +
Buffer.from(clientId + ":" + clientSecret).toString("base64"),
"Content-Type": "application/x-www-form-urlencoded",
},
params: {
grant_type: "refresh_token",
refresh_token: refreshToken,
},
})
.then((response) => {
res.json({
accessToken: response.data.access_token,
expiresIn: response.data.expires_in,
});
})
.catch((error) => console.log(error.statusCode));
});
and a function on the client-side whenever I call, it will return the token but when I console log the code below, it prints out non-stop
here the code:
let getData = () => {
axios.request("http://localhost:3001/refresh_token").then((response) => {
SetToken(response.data);
setExpires(response.data.expiresIn);
console.log(token);
});
};
useEffect(() => {
const data = setInterval(() => {
getData();
}, 1000);
});
return (
<div className="spotify">
<button
onClick={() => {
getData();
}}
>
click
</button>
</div>
);
thank you
Your useEffect is missing a dependency array, so it will be called for every rerender of your component (and even worse, it will create a new interval timeout that's never cleared every time). I'll bravely assume setToken and setExpires are state setters for the component, so those would cause rerenders.
You'll need
useEffect(() => {
const dataTimer = setInterval(() => {
getData();
}, 1000);
return () => clearInterval(dataTimer);
}, []); // <- this empty dependency array
so it will only be called once when your component mounts, and so the interval is cleaned up when it unmounts.
(getData should also be in that dependency array, but since you're not seemingly memoizing it using useCallback, that would negate the benefit of the empty array, since the identity of getData would also be changing on each render...)

How can stop infinite loop in useeffect hook

const [list, setlist] = useState([]);
const token = localStorage.getItem("token");
const requestOptions = {
method: "GET",
headers: { authorization: `Bearer ${token}` },
};
useEffect(() => {
fetch("http://localhost:3000/recruiter/postedjobs",requestOptions)
.then((response) => response.json())
.then((data) => {
setlist(data.data);
});
});
I am working on the react js where i have to show the list of user after the page render so i use the useeffect hook what when i write the useeffect hook it call the api infinite time how can stop this. if i add the blank dependencies [] it show requestoptions are missing from dependencies
Pass an empty array as a second argument to useEffect Hook.
useEffect( ()=>{
console.log(‘hello’);
}, [] );
If you would leave the second argument of the Effect Hook empty, you would run into an infinite loop because the Effect Hook always runs after the state has changed. Since the Effect Hook triggers another state change, it will run again and again to increase the count.
You sould pass requestOptions as second argument
const [list, setlist] = useState([]);
const token = localStorage.getItem("token");
const requestOptions = {
method: "GET",
headers: { authorization: `Bearer ${token}` },
};
useEffect(() => {
fetch("http://localhost:3000/recruiter/postedjobs",requestOptions)
.then((response) => response.json())
.then((data) => {
setlist(data.data);
}, [requestOptions]);
});

How to Render Data from a POST API call in React

I'm trying to figure out how to code my current API call so that I can access each field from the API call and render it, then be able to use it across multiple components. I'm using the QuickBase API call that only allows POST to pull field values. I've been out of the game for a couple of years and can't figure out how to accurately render these to be able to be used in other components by importing the api.js file. The project is a React within Electron to pull QuickBase data, and be able to create Line Charts (7 on one page) to show a job cost/hours and the jobs included departments cost/hours. All of my data is in quickbase, I just can't figure out how to get it over to react and able to actually use it!
Here is my API call:
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXXXX_XXXXX_XXXXXXXXXXXXXXX',
'Content-Type': 'application/json'
}
let body = {"from":"bpz99ram7","select":[3,6,80,81,82,83,86,84,88,89,90,91,92,93,94,95,96,97,98,99,101,103,104,105,106,107,109,111,113,115,120,123,224,225,226,227,228,229,230,231,477,479,480,481],"sortBy":[{"fieldId":6,"order":"ASC"}],"groupBy":[{"fieldId":40,"grouping":"equal-values"}],"options":{"skip":0,"top":0,"compareWithAppLocalTime":false}}
fetch('https://api.quickbase.com/v1/records/query',
{
method: 'POST',
headers: headers,
body: JSON.stringify(body)
})
.then(res => {
if (res.ok) {
return res.json().then(res => console.log(res));
}
return res.json().then(resBody => Promise.reject({status: res.status, ...resBody}));
})
.catch(err => console.log(err))
Any help would be greatly appreciated as I've been struggling on this for awhile! Right now I'm able to get all the correct data in the Console. But don't know how to go about rendering it on my application for actual use.
Thanks!
I think you should put your code inside a function and call that function from the component where you need the data, something like
import React, { Component } from 'react'
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXXXX_XXXXX_XXXXXXXXXXXXXXX',
'Content-Type': 'application/json'
};
class App extends Component {
state = {
data: null,
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
let body = {"from":"bpz99ram7","select":[3,6,80,81,82,83,86,84,88,89,90,91,92,93,94,95,96,97,98,99,101,103,104,105,106,107,109,111,113,115,120,123,224,225,226,227,228,229,230,231,477,479,480,481],"sortBy":[{"fieldId":6,"order":"ASC"}],"groupBy":[{"fieldId":40,"grouping":"equal-values"}],"options":{"skip":0,"top":0,"compareWithAppLocalTime":false}}
fetch('https://api.quickbase.com/v1/records/query', {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
}).then(response => {
if (response.ok) {
return response.json().then(res => {
this.setState({
data: res,
})
});
}
return response.json().then(resBody => Promise.reject({status: response.status, ...resBody}));
}).catch(err => console.log(err))
}
render() {
const { data } = this.state;
if (data === null) return 'Loading...';
return (
<div>
{/* Do something with data */}
</div>
);
}
}
export default App;
Check the Docs, you can send the JSON in the props of the component to render it.
You can modify your code following this example.
sandbox
import { useEffect, useState } from "react";
async function apiCall() {
return await new Promise((resolve, reject) => {
// Api Call
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => resolve(json));
});
}
const TestApp = () => {
let [data, setData] = useState({ Text: "Before api call." });
useEffect(() => {
(async () => {
let res = await apiCall();
res.Text = "After api call.";
setData(res);
})();
}, []);
return (
<div>
UserId: {data.userId} id: {data.id} title: {data.title}{" "}
completed: {data.completed}
</div>
);
};
module.exports = TestApp;

Empty array in useEffect vs useCallback in parent for server api call

I am making an api call to my server on component mount, so at first I did it with empty array in useEffect:
useEffect(() => {
fetch('http://localhost:8080/api/chat/auth/getChats?pageSize=3',{
headers: {
'Authorization': localStorage.getItem('Authorization')
}
}).then(data => data.json())
.then(data => setUserChats(data))
}
}, [])
But there is a warning for dependencies and it says to use callback in parent, but I don't understand what is the differnce in memorizing the method with empty array in the parent vs just empty array in effect:
Parent.js:
const setChatsCallback = useCallback(chats => {
setChats(chats)
}, [])
Child.js:
useEffect(() => {
fetch('http://localhost:8080/api/chat/auth/getChats?pageSize=3',{
headers: {
'Authorization': localStorage.getItem('Authorization')
}
}).then(data => data.json())
.then(data => setUserChats(data))
}, [setChatsContainer])
Also I have seen places where they first define the method in the useEffect and then call it is there a diffrenece between that and just put the logic in the useEffect directly:
useEffect(() => {
async function doRequest() {
fetch('http://localhost:8080/api/chat/auth/getChats?pageSize=3',{
headers: {
'Authorization': localStorage.getItem('Authorization')
}
}).then(data => data.json())
.then(data => setUserChats(data))
}
doRequest()
}, [setChatsContainer])
And if there is a differrence can I just do it with self executing function:
useEffect(() => {
(async() => {
fetch('http://localhost:8080/api/chat/auth/getChats?pageSize=3',{
headers: {
'Authorization': localStorage.getItem('Authorization')
}
}).then(data => data.json())
.then(data => setUserChats(data))
smoothscroll.polyfill()
setChatsContainer(chatsContainer)
})()
}, [setChatsContainer])
Your useEffect is using setUserChats which comes from props. As props can change react assumes your effect should run whenever this callback changes. Thus the warning for missing dependencies.
Defining an async callback inline and calling it in a useEffect is required if you want to use the async/await syntax instead of Promise.then() as the callback given to useEffect itself is not allowed to be async. If you are only using .then() you do not need it. There is no practical difference between defining an anonymous arrow function and directly calling it and defining a named function and calling it.
Those are both doing the same:
useEffect(() => {
fetch('http://localhost:8080/api/chat/auth/getChats?pageSize=3',{
headers: {
'Authorization': localStorage.getItem('Authorization')
}
}).then(data => data.json())
.then(data => setUserChats(data))
}, [setUserChats])
and
useEffect(() => {
(async() => {
const response = await fetch(
'http://localhost:8080/api/chat/auth/getChats?pageSize=3',
{
headers: {
'Authorization': localStorage.getItem('Authorization')
}
}
)
const data = await response.json()
setUserChats(data)
})()
}, [setUserChats])
Some people (myself included) prefer the async/await syntax as it is easier to follow the control flow. Note that this is an opinion.

Categories

Resources