How to decide when to rerender in React - javascript

I'm creating a small project with the mern stack. In the homepage of this project, you can see the things that the current user has. THe problem is that the list can change in every moment, because other user can happend to that list other stuff. SO, to make it refresh, I put it in the useEffect hook. The problem now is that my server is reciving a tons of request, and always the same one. I don't know if is the case to set a timer that rerender after x second, to make the work of my server lighter, or there is a way to remake the request to the server in some case. Here is the small code that manage the request:
const [file, setFile] = useState([]);
useEffect(() => {
axios.post("http://127.0.0.1:5050/file/getfilesbycreator",{creator:localStorage.getItem('user')})
.then(res => {
setFile(res.data);
})
.catch(err => console.log(err))
})
If someone has any suggestion, please tell me. I read something about rerender react, but I didn't found out better way then a timer, or something similar.Thanks

As the second parameter of your useEffect, you have to indicate which variable you are looking for to rerender.
If you want the useEffect to render only one time, you have to put [] as second parameter like that :
useEffect(() => {
axios.post("http://127.0.0.1:5050/file/getfilesbycreator",{creator:localStorage.getItem('user')})
.then(res => {
setFile(res.data);
})
.catch(err => console.log(err))
}, [])
If you have a variable that can change, for example 'file', just add it in the array :
useEffect(() => {
axios.post("http://127.0.0.1:5050/file/getfilesbycreator",{creator:localStorage.getItem('user')})
.then(res => {
setFile(res.data);
})
.catch(err => console.log(err))
}, [file])

Related

JavaScript the rest of function executes before API calls

I have a problem with a simple function which where I want to call an API and then do something with the response. Basically, I just want to set my react component state to the response I get and then navigate to the other page The problem is that my code executes another part of the function before an API call is finished, and I end up on another page with console.log of undefined
There is my function:
const startNewGame = () => {
GameService.startGame()
.then((response) => {
setGame(response.data);
console.log(game);
navigate('intro');
})
.catch((e) => {
console.log(e);
});
};
I can wrap my navigate into if(!game !== undefined) but then I have to click two or more times on a button.
Thank you all guys for help :)
You probably do several things incorrect at the same time, so to understand what exactly is wrong might take some time. Take these steps to debug your code:
Make each .then call do only one thing (and stick to that principle in other cases). You could chain as many .then as you like. Moreover you could return data from one .then to the next, so your code might look like this:
GameService.startGame()
.then(response => response.data)
.then(data => {
setGame(data)
})
.then(() => {
console.log(game);
navigate('intro');
})
.catch((e) => {
console.log(e);
});
Understand your components composition. Where exactly are you saving your response? is it just local component useState or some Context Api state that wraps the app? When you navigate to "other page" your "current page" state will be unavailable to "other page" unless you keep the data somewhere up in the tree when both pages could access that.
For further references keep in mind that setGame is asynchronous, so you need to wait for the state to be updated to make sure that its updated.
Try this:
const startNewGame = () => {
GameService.startGame()
.then((response) => {
setGame(response.data);
// game is not updated yet
// console.log(game); <- Remove this line
// navigate('intro'); <- Remove this line
})
.catch((e) => {
console.log(e);
});
};
useEffect(()=>{
if(game !== undefined){
navigate('intro')
}
}, [game])

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.

How can I fetch the real-time json data in more efficient and less buggy way?

I am trying to fetch the real-time JSON data from the server on my react app. The code that I write works rarely, continuously fail to fetch and also it creates too much request to the server which makes the browser really slow. How can I fetch the real-time data in a more efficient and less buggy way? Thanks in advance.
I used Fetch API and setInterval to request the data to the server and used react hooks to update the data. But it is doing too much request than I thought and also occurs a speed problem.
const [state, setState] = React.useState(0)
const devicePos = function () {
fetch('http://192.168.10.233:34599/')
.then(response => response.json())
.then(response => {
setState(response[0].x)
console.log(response[0].x)
})
.catch(error => {
console.log(error);
});
}
setInterval(function(){
devicePos()
}, 500);
I hope the real-time data updates faster.
instead of putting it in setInterval, asynchronous code should go in the useEffect hook. In react you need to do your async calls in lifecycle methods because they have side effects, as you were probably already experiencing. The useEffect hook is similar to componentDidMount in classes. The difference is that it comes with more in the box, meaning it handles all three lifecycle methods, componentDidMount, componentDidUpdate, componentWillUnmount
useEffect(() => {
devicePos();
}, []);
Here is the docs as to how it works
I don't think setInterval is a good way for polling data. Some responses might be slower than 500 ms and you might get older results later.
Whenever you poll, you should wait for previous response.
const [state, setState] = React.useState(0)
const [timer, setTimer] = React.useState(null)
const [isMounted, setIsMounted] = React.useState(false)
async function updateDevicePosition () {
try {
const result = await fetch('http://192.168.10.233:34599/')
const data = await result.json()
setState(data.x)
} catch (e) {
console.error(e)
}
clearTimeout(timer)
setTimer(setTimeout(updateDevicePosition, 200))
}
useEffect(() => {
if (!isMounted) {
updateDevicePosition()
setIsMounted(true)
}
})
That being said, this is a lot of requests for such job. Preferred way should be to use a socket where server is leading data and only sending messages to your client when device position is changed. Depending on when you'll use this position information, your app will be using lot's of resources
You could have it only resubmit the fetch when the response comes back (plus some delay).
const devicePos = () => {
fetch()
.then( res => res.json() )
.then( res => {
updateData();
} )
.catch( err => {
showError(err);
})
.finally( () => {
setTimeout( devicePos, 500 );
})
}
Otherwise, you might end up bogging down a server that is too slow.
The finally ensures that regardless of the api response (or timeout), the request is sent again.

Reusing Fetch with Different Pieces of State in React (Flux)

I have this function that is in the top level component that I'd like to reuse but on a different piece of state. In the following code sample, I'm using it on userData, but I'd like to be able to reuse it on a piece of state called repoData, either within the same component it's child, or outside. Just not sure how to go about it, since, if I do something like pass it the states as args, setting the state would throw an error because it would look like: this.setState({this.state.userData: data}).
fetchUserData(params) {
fetch('https://api.github.com/users/' + params)
.then(response => {
if (!response.ok) {
throw Error("Network failure")
}
return response;
})
.then(data => data.json())
.then(data => {
this.setState({
userData: data
})
}, () => {
this.setState({
requestFailed: true
})
})
}
Your question is more about refactoring your code I believe. Here you can look at it as moving out the logic of fetching user data asynchronously. Your component need not know who(fetch, axios) is doing the work of fetching the data for it. Also it is not required for a component to know from where(https://api.github.com/users/' + params) to get this data.
One possible way of doing it is to move your fetch call in a separate function which will return the data to your component after fetching it asynchronously. Then it is responsibility of component to handle the data the way it want to.
So as you can see the key point here is identifying and separating out the responsibilities of each piece of code. Advantages of doing this will be clean code structure, less number of lines, maintainable code and most importantly easily testable code.
I hope this solves your problem. If you are interested to learn about such techniques more you can read about DRY principle and refactoring techniques.
Edit 1:
I have created this code sample for your reference
What are JavaScript Promises: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Some samples for fetch: https://davidwalsh.name/fetch
I personally recommend using axios. You can see comparison of fetch and axios here: https://medium.com/#thejasonfile/fetch-vs-axios-js-for-making-http-requests-2b261cdd3af5
If I'm understanding you correctly
you should move the function to a file x.js
it should accept an other param which is the name of the new (piece) of state
it should also accept a param for the this.setState of the component calling it.
import it and use it in componentDidMount as normal
```
export const fetchUserData(params,setState , stateParam) {
fetch('https://api.github.com/users/' + params)
.then(response => {
if (!response.ok) {
throw Error("Network failure")
}
return response;
})
.then(data => data.json())
.then(data => {
setState({
[stateParam]: data
})
}, () => {
setState({
requestFailed: true
})
})
}
you can call it as follows
import { fetchUserDate } from "./path/to/x"
fetchUserDate("param" , this.setState, "xOnState")

React Native Fetch does not render response until after clicking screen

So I've been building an app that uses the Fetch API to make a request. Unfortunately, I think the code shown in the Facebook docs are (may be?) incorrect.
Problem
When starting the app, Fetch makes a call to a URL to pull JSON data.
componentDidMount(){
return fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
data: JSON.stringify(responseJson)
})
})
.catch((error) => {
console.error(error);
});
}
This is similar to the code that Facebook docs give as an example.
The problem is that when I start the app, the data doesn't render in the render function.
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
var data = this.state.data;
return this.renderData(data);
}
At least, it doesn't render until after I click/touch the screen. Once I touch the screen, the data renders. Otherwise, it just stays in a perpetual "loading" state.
Solution?
I remembered that sometime in the past, I had to bind event handlers to their respective controls (this.handleClick = this.handleClick.bind(this), for example)
And that got me thinking: Where is this in the promise request? Where does this point to?
componentDidMount(){
return fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseJson) => {
this.setState({ <--- **Where are you going?**
isLoading: false,
data: JSON.stringify(responseJson)
})
})
I console.logged "this" within the responseJson promise, and it does point to the component, but I'm not convinced. With it being buried inside of the promise, I don't think the this.setState function is actually able to set the component's State.
So I made a variable before the fetch request.
const that = this;
return fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseJson) => {
that.setState({
isLoading: false,
data: JSON.stringify(responseJson)
})
})
Now I'm not a software guru. I've been teaching myself this stuff for the last couple of years, so my logic is off half the time.
So if my reasoning is correct or incorrect, please let me know! What I do know is that this is working for me; once the data is fetched, it automatically sets the state and reloads, showing me the JSON data when rendered.
Any thoughts? Am I completely off here? Am I missing anything?
I saw a problem like yours. It's not about your code. Are using the app in the Remote JS debugging mode? If so, disable it. If it didn't work, try to install a release version.
(Posted on behalf of the OP).
Apparently it was due to running the app in debug mode. Running the app with this.setState works just fine when not in debug mode.

Categories