How to stop executing a command the contains useState? - javascript

This is my code which sends a GET request to my backend (mySQL) and gets the data. I am using useState to extract and set the response.data .
const baseURL = 'http://localhost:5000/api/user/timesheet/13009';
const [DataArray , setDataArray] = useState([]);
axios.get(baseURL).then( (response)=>{
setDataArray(response.data);
});
But useState keeps on sending the GET request to my server and I only want to resend the GET request and re-render when I click a button or execute another function.
Server Terminal Console
Is there a better way to store response.data and if not how can I stop automatic re-rendering of useState and make it so that it re-renders only when I want to.

As pointed out in the comments, your setState call is triggering a re-render which in turn is making another axios call, effectively creating an endless loop.
There are several ways to solve this. You could, for example, use one of the many libraries built for query management with react hooks, such as react-query. But the most straightforward approach would be to employ useEffect to wrap your querying.
BTW, you should also take constants such as the baseUrl out of the component, that way you won’t need to include them as dependencies to the effect.
const baseURL = 'http://localhost:5000/api/user/timesheet/13009';
const Component = () => {
const [dataArray , setDataArray] = useState([]);
useEffect(() => {
axios.get(baseURL).then( (response)=>{
setDataArray(response.data);
});
}, []);
// your return code
}
This would only run the query on first load.

you have to wrap your request into a useEffect.
const baseURL = 'http://localhost:5000/api/user/timesheet/13009';
const [DataArray , setDataArray] = useState([]);
React.useEffect(() => {
axios.get(baseURL).then((response)=>{
setDataArray(response.data);
})
}, [])
The empty dependency array say that your request will only be triggered one time (when the component mount). Here's the documentation about the useEffect

Add the code to a function, and then call that function from the button's onClick listener, or the other function. You don't need useEffect because don't want to get data when the component first renders, just when you want to.
function getData() {
axios.get(baseURL).then(response => {
setDataArray(response.data);
});
}
return <button onClick={getData}>Get data</button>
// Or
function myFunc() {
getData();
}

Related

React Update Profile for user through a form

I'm trying to build a Update profile form where a user can change his details and onSubmit the new data will be sent to PUT/users end point to update the details in the database. The form will have the old values pre filled (gotten through a GET request to the same /users endpoint). To access this endpoint we also need to send a basic auth header with email and password. Now I'm making two fetch requests one to get the existing details and one to PUT the new details. My first GET request is successfully made and I can see the data prefilled but my second POST request doesn't work. What am I doing wrong?
Here is how my code looks. https://codesandbox.io/s/dawn-breeze-itinq?file=/src/App.js
const getUsers = async () => {
let myHeaders = new Headers();
myHeaders.set('Authorization', 'Basic ' + credentials);
const requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow',
};
try {
const response = await fetch(`${APIlink}/users`, requestOptions);
const result = await response.json();
setData(result);
} catch (err) {
setErr('Incorrect Password. Please Retry.');
}
};
useEffect(() => {
getUsers();
}, []);
You useEffect gets called on Every render as you have not mentioned any dependency array. So what happens in your case is,
your component renders --> useEffect gets called --> make api call --> you set state --> component re-renders --> useEffect gets called --> make api call and this cycle continues forever .
useEffect(() => {
....
}); => this useEffect will trigger on first render and every re-render
useEffect(() => {
...
}, []); => this will trigger only for the first time when the component is mounted
useEffect(() => {
...
}, [value]); => triggers on first render and whenever the value changes
If you are familiar with lifecycle methods in class based components, you usually do API calls in componentDidMount lifecycle method. This method is called only once after the first render. More on this: https://reactjs.org/docs/react-component.html#componentdidmount
In your code, you are using useEffect which is more or less like the componentDidUpdate lifecycle method, which is called on every render. this is why,
The page loads and I can see an endless string of GET requests in the network tab.
More on this: https://reactjs.org/docs/react-component.html#componentdidupdate
The solution would be to make your useEffect hook behave like a componentDidMount lifecycle method, which is essentially telling the useEffect hook to run only once.
If we examine a hook, it is made up of two components:
Callback function (required)
Dependency list of props (optional)
which looks like this:
useEffect(Callback, [Dependencies]);
An example would be:
useEffect(() => {
console.log(props.color)
}, [props.color]);
So essentially we are saying that whenever the prop color is changed, we want to log the value of color.
Seeing as how this is, if we pass an empty array of dependencies, the useEffect hook will only run once and that is what you should do. Do note that if a dependencies array is not passed, it will still behave like componentDidUpdate lifecycle method i.e it will be executed on every render.
Solution if you just skipped to this part is:
Pass an empty dependencies list in your useEffect hook.
More on hooks:
https://reactjs.org/docs/hooks-effect.html#explanation-why-effects-run-on-each-update
https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

React hooks: callback as prop re-render every hooks depending on it when it's called

In the example below, I'm getting in a loop when I call the onError prop in fetchItems(). I don't understand why, when called, it triggers hooks depending on it. How can I fix this? Thanks!
const Component = ({onError}) => {
const [items, setItems] = useState([]);
const itemsRef = useRef(items);
const fetchItems = useCallback(() => {
const [first] = itemsRef.current;
fetchNewItemsSince(first || 0).then((newItems) => {
setItems((oldItems) => [...oldItems, ...newItems]);
}).catch(onError);
}, [onError]);
// Update ref to dispose closure on `items` state
useEffect(() => {
itemsRef.current = items;
}, [items]);
// Call once on mount
useEffect(() => {
fetchItems();
}, [fetchItems]);
// Make an interval
useEffect(() => {
const id = setInterval(fetchItems, ONE_MINUTE);
return () => {
clearInterval(id);
};
}, [fetchItems]);
};
Try setting the initial state with a function
Const [foo, setFoo] = useState(() => ‘foo’)
Your useCallback for that state instance probably runs once, correct me if I’m wrong, so if you set a function for useState it will only run once, consider is a component did mount but no update.
Maybe this is practice but I have never called something like [onError] without setting an error state, because that’s what I think recognizes it.
So React is this great thing that renders certain components etc. UseEffect is great, I personally don’t use it to change the state, that for me is usually done in the JSX.
I would do like a onClick handler with the UseState method and watch for changes there. Instead your running a function that runs a setState event and watches for an event.
Let me know if that works or not if you need any explanation.
As I said in the comments, onError is triggering a re-render on the parent and therefore the children will also render again.
Someone suggested removing onError from the useCallback array of dependencies. Although it might work, it is considered a bad practice because can lead to memory-leak. Have you tried to remove the useCallback wrap around your function?

React useEffect hook dispatch actions on chain

First of all i am new to react hooks. hence please bear if it's silly.
So, i want to dispatch actions one after another in useEffect hook (on Load).
Also my second dispatch needs data from first dispatch.
Here is what i have written which is not working.
const userData = useSelector((state) => state.user.data);
useEffect(() => {
dispatch(fetchUser("someUserId")).then(() => {
dispatch(fetchProjects("someUserId", userData.settings.startDate));
});
}, []); // on load
In the above code, userData.settings is fetched from first dispatch. When i run the code i get an error saying userData.settings is not defined.
Please let me know, what is wrong here.
Thanks in advance

how to use setInterval with redux-thunk?

I'm using redux-thunk to manage async functions, and I want to use setInterval within an action creator, this is my code:
export const startLobbyPolling = () => dispatch => {
const pollTimer = setInterval(() => {
dispatch(fetchLobby);
}, POLL_TIME);
dispatch({ type: START_LOBBY_POLLING, payload: pollTimer });
};
fetchLobby is another action creator that simply fetch a request and store its data.
but surprisingly it's not working as it only shows START_LOBBY_POLLING action in redux debugger tool and nothing happens afterward. I would appreciate it to know how to use setInterval with redux.
I would suggest to not use redux as a polling manager.
Instead you should have a component / container that does that for you. The reason being is, that when the component will get unmounted, also your polling will be properly canceled
const PollContainer = ({ fetchLoby, children }) => {
useEffect(() => {
const ptr = setInterval(fetchLoby, POLL_TIME)
return () => clearInterval(ptr)
}, [fetchLoby])
return children
}
you can now use your PollContainer and as long as it is mounted, it will keep fetchLoby.
Seems like i should had called the function not just pass it as an argument to dispatch
dispatch(fetchLobby); -->
dispatch(fetchLobby()); and it works now
There's nothing particular about using setInterval with redux-thunk. It should work just as it would anywhere else. You need to see the callback function which has been used in the setInterval.

Updating State In React With React Hooks

I've been having a difficult time updating state inside of my React application lately using the useState hook.
If I define my state in a provider as -
const [session, setSession] = useState({});
const [sessionId, setSessionId] = useState(0);
And then try to set it using the setSession
setSession(response.data);
It always comes back as the default value. This all happens inside of the provider component - i.e. I'm trying to access the information within other functions in that same provider.
However, if I store that data in localStorage, for example, I have no issues accessing it whatsoever.
localStorage.setItem("session", JSON.stringify(response.data));
I've verified that the information coming from the server is an object, and that the correct data. There's no errors or promises, just the object containing the response. If I put the snippet the setSession(response.data) and localStorage.setItem("session", JSON.stringify(response.data)) next to each other, the setSession leaves the session value as {} whereas setting the local storage works perfectly. Both are using the same API response, same data
// This is the method on my component that I'm trying to use to update the state
const updateStateAfterSessionInitialization = async data => {
setSession(data)
localStorage.setItem("session", JSON.stringify(data));
setSessionId(data.id);
// both of these log a value of `{}` and `0` despite the values being set above
console.log(session)
console.log(sessionId)
closeStartSessionModal();
// If I redirect my application like this, it works fine. The ID is the correct value being returned by the server
window.location = "/#/reading-sessions/" + data.id;
}
// All of this code below is wrapped in a function. None of this code is being executed at the top level
let response = await axios({
method: method,
data:data,
url: url,
headers: headers
});
await updateStateAfterSessionInitialization(response.data);
Literally all of the data is working perfectly fine. The server responds with the correct data, the correct data is stored the session in local storage. If I redirect using the ID from the object from the server, it works fine. But if I try to update the state of the component and access the state properly, it just just doesn't work, and as a result I'm having to try to find ways of working around setting the state.
Is there something that I'm misunderstanding here?
The code that I'm working with is here - https://github.com/aaronsnig501/decyphr-ui/commit/ef04d27c4da88cd909ce38f53bbc1babcc3908cb#diff-25d902c24283ab8cfbac54dfa101ad31
Thanks
The misunderstanding you have here is an assumption that state updates will reflect immediately which is incorrect
State update is async and will only refect in the next render cycle. If you try to update state and log it in the next line, you wouldn't see and updated state
// This is the method on my component that I'm trying to use to update the state
const updateStateAfterSessionInitialization = async data => {
setSession(data)
localStorage.setItem("session", JSON.stringify(data));
setSessionId(data.id);
// both of these log a value of `{}` and `0` despite the values being set above
console.log(session) // This is expected to log previous value
console.log(sessionId) // This is expected to log previous value
closeStartSessionModal();
window.location = "/#/reading-sessions/" + data.id;
}
Now localStorage is synchronous and hence its update is reflected immediately
If you wish to see if the update to state was done correctly you could write a useEffect that depends on it
useEffect(() => {
console.log('State session/sessionId updated');
}, [session, sessionId])
Now depending on what you are trying to achieve you would need to modify your code in line with the above statement that state update calls are asynchronous.
Also setSession doesn't return a promise so you can't just use async await with it. You need to make use of useEffect to take an action on state update
For Example:-
import React, { useState, useEffect } from "react";
import axios from "axios";
function App() {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await axios("https://jsonplaceholder.typicode.com/posts");
console.log(result.data);
setData(result.data);
};
fetchData();
}, []);
return (
<ul>
{data.map(res => (
<li>{res.title}</li>
))}
</ul>
);
}
export default App;
Check your type of response.data and defined the types
array - []
objects - {}
string - ""
number - 0 in useState
setState is async, so new value will apply on the next rerender not in the next line.

Categories