Why react lifecycle work everything multiple times - javascript

i have problem in react and long time i can't figure out with this.
I can't understand what happened.
What a scenario:
My app using React and Redux. I keep all my state in redux.
i set some dataRefreshed state to redux state for handling re-rendering page when data updated from api.
i'm using componentWillReceiveProps lifecycle method.
in my redux state
let initialState = {
dataRefreshed: false
}
when my request starting, in redux
case START_REQUEST:
return {
...state,
dataRefreshed: false
};
case SUCCESS_REQUEST:
return {
...state,
dataRefreshed: true
};
So in my component when i make request and get from api new data:
componentWillReceiveProps(nextProps) {
if (nextProps.Reducer.dataRefreshed) {
apiCall();
}
}
so if thinking with logic:
1- when my request start and request getting ok status, my dataRefreshed setting true
2- and here nextProps.dataRefreshed and this.props.dataRefreshed not equal.
Till here everything work well. my Condition working and apiCall() runned.
but apiCall function runned 10 times
why my state toggling one time so my state turning one to to true from false. But inside condition my function calling million times.
i can't understand what is the logic here.
I'm seriously think anymore living react because of that

This is what your code actually does:
set dataRefreshed=true
trigger props change
call apiCall() (cause dataRefreshed==true)
START_REQUEST: setting dataRefreshed=false
trigger props change (does nothing cause dataRefreshed==false)
apiCall got response from server,
SUCCESS_REQUEST: setting dataRefreshed=true
goto 2 (infinite loop)
You should introduce some flag like shouldRefresh, which will be set to false once data is fetched and not automatically set back to true unless it is meant to.

Is not that it is doing it twisted. The problem is componentWillReceiveProps method, that’s why react remove it to avoid this type of things

Related

componentDidUpdate get triggers at multiple screens

Im using redux-thunk for API calling. While response coming back from the server, I catch them at componentDidUpdate with the simple validation as follow
async componentDidUpdate(prevProps) {
if (this.props.successCalling && !prevProps.successCalling) {
executeSomeCode();
}
}
The problem is, I'm having 2 same validation at 2 different screens, when responses are back from server componentDidUpdate both the validations are met. Hence it run both the executeSomeCode() at both different screens. How am I suppose to handle this kind of scenario?
While calling the service for a particular screen you can use a class level variable which can act as a switch like - true/ false, where this.switch will be set to false initially. on calling the service just update that class variable to true and add that variable to the if logic check as mentioned in the above example.
This way every screen will have its own switch which can be used to determine if the service call is for that particular screen or not.

Updating redux state does not trigger re-render (nor does it update the state)

I'm pretty new to Redux and i'm encountering some problems with it.
I'm creating a list of items in a component, sending it to redux state and then i want to read from that redux state and display the items in a different list component.
The creation part works as i can console.log and getState() without problems (i am seeing the changes in Redux State).
My problem is that my component state does not change, nor does it re-render.
And now some code ->
this.state = {
initialItems: this.props.SharepointItems,
}
And at the end
const mapStateToProps = (state) => {
return {
SharepointItems: state.listItems,
}
}
export default connect(mapStateToProps)(SharePointList);
I even tried something like this in my componentDidMount() ->
store.subscribe(() => {
this.setState({ initialItems: this.props.SharepointItems });
console.log("updating state");
});
From what i've read i shouldnt need to update the state manually while using redux, or am i wrong?
EDIT: Since my list doesnt throw an error if i console.log i can see that the array is empty (which is what i defined in the Redux state). Is there something that i should be doing to get the new state ? Seems like its getting the immutable state or something like that (the empty array).
Edit2: Found the problem (or part of it). It appears as my state is 1 event behind. So redux contains the array with 4 items, the component state is empty. If i do a dispatch from the browser i get 5 items (as expected) in redux, but 4 in state (it finally shows not empty).
Also my code was a bit bugged (i was passing the entire array instead of items in the array).
I've changed it to
result.map((item) => {
store.dispatch(addListItem(item));
});
And it started rendering. The problem is it displays items from 0 to 2 (4 in array), but the last one is left behind. Once again if i do another dispatch from the browser i get item 3 rendered, but 4 (the last one added) is only in redux state and does not update the list state.
Also...is it a good idea to do it like this? My list might have 1000 items in the future and i'm not sure that dispatch is a good solution (i need to make an API call to get the items first, which is why i'm using dispatch to populate redux).
Updated with reducer ->
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_LISTITEM:
return { ...state, listItems: [...state.listItems, action.payload] };
case ADD_SPOTOKEN:
return { ...state, spoToken: action.payload };
default:
return state;
}
};
export default rootReducer;
Found something else thats a bit weird. I am also using React Router. As i said my list displays only 3 of the 4 items in my Redux State array. If i navigate to a different page and then back to the list page it actually renders all 4 of the items.
I believe your problem stems from the fact that you have "forked" SharepointItems off of props and set it to the component's local this.state. Unless you really need to, I'd recommend not doing that. Just use this.props. Dan Abramov (author of Redux) recommends this as a general principle too.
By forking props onto state, you create two sources of truth regarding the state of SharepointItems, namely, this.state.initialItems and this.props.SharepointItems. It then becomes your responsibility to keep this.state and this.props in sync by implementing componentDidUpdate (that is why you're not seeing it update). You can avoid all the extra work by just using the data that flows in from props.
The connect function will re-render your component with new props whenever you update the redux store. So in your render method, just refer to this.props.SharepointItems and not this.state.initialItems. Then you should be good to go, that is, assuming you've implemented your reducer(s) and store configuration properly.
I actually fixed this by mistake. I was planning on leaving it for the end and find a workaround for it, but i somehow fixed it.
I was importing the list component (which had the redux state props) in a Page (react-router). The page wasn't connected since it wasn't ready yet. Apparently after connecting the page (parent component which holds the list) everything works fine and i can see all 4 of my items (instead of seeing 3 without having the parent connected).
I wonder if this is intended...

re-render component without setting state react js

I am deleting a record from db, for this I am calling an API. When I received an
API response of a successful deletion, I need to re-render all the component again like reload does. I tried it with this.forceUpdate and shouldComponentAgain but no luck.
I also tried with componentDidUpdate, it works but it is calling API infinite times. Below is my code how I used componentDidUpdate:
componentDidUpdate(){
let newThis = this;
getAccounts().then(function(response){
if(response.status===200){
newThis.setState({
Accounts:response.data
})
}
});
}
Please tell me the way to re-render like reload do, but without re-loading the whole page.
When using componentDidUpdate, you should always have a conditional setState which denotes that you need to perform something because the current state or current props is not equal to previous state or props.
componentDidUpdate always gets called whenever your component has updated. In your case what is happening is that you are calling setState without any condition which updates your component, and setState is called again causing an infinite loop in updating the component.
You should have something like this check here:
componentDidUpdate(prevProps, prevState){
let newThis = this;
if(newThis.props.{some-variable} !== prevProps.{some-variable}) {
getAccounts().then(function(response){
if(response.status===200){
newThis.setState({
Accounts:response.data
})
}
});
}
}
Adding conditional setState is very important here else you will end up in an infinite loop.
As per the official docs as well:
You may call setState() immediately in componentDidUpdate() but note
that it must be wrapped in a condition or you’ll cause an infinite
loop. It would also cause an extra re-rendering which, while not
visible to the user, can affect the component performance. If you’re
trying to “mirror” some state to a prop coming from above, consider
using the prop directly instead.
Hope it helps.
If you want to render the component again, then change the props from the parent. If props change then child component automatically going to render. And by this features, you can also render the selective component.

React function after dom has finished rendering

I have a map of the world and whenever you click on a particular country, it pings and API and gets schools in that country. React then uses leaflet to display all the dots(GEOJSON). After the schools are loaded I have a Dock type thing that will pop up. The problem is that I need to know when the react is done updating. I tried using the react lifecycle methods in the Map.jsx file I have the following code to try to see when the GEOJSON is done rendering:
componentWillUpdate() {
console.log('CWU');
}
componentDidUpdate() {
console.log('CDU');
}
But in the console I get the following printouts:
CWU, CDU, CWU, CDU, CWU, CDU
So both functions run 3 times, for another country both functions run twice. So I cant put the function that brings up the Dock in either componentWillUpdate or componentDidUpdate because I would need to run the function after the 3rd 'CDU' or for the other country after the second 'CDU'. Is there anyway way to know when react has finished rendering ?
I found a way to solve it. I'll try my best to explain it:
I have a state variable called
didUpdate = false
The API changes a particular entry in the props
so in componentWillUpdate I check to see if the next state is different than my current one if so I set didUpdate to false. This makes it so that it doesn't display.
Then in my componentDidUpdate I check to see if previous state is different than my current one if so I then set didUpdate to true. This then makes it so the dock is shown.
Here is some code:
componentWillUpdate(nextProps, nextState) {
if (nextProps.X !== this.props.X) {
this.setState({
didUpdate: false,
})
}
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.X !==this.props.X) {
this.setState({
didUpdate: true
})
}
}
You can use
componentWillUnmount()
componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. Perform any necessary cleanup in this method, such as invalidating timers, canceling network requests, or cleaning up any subscriptions that were created in componentDidMount().

React native: State seems to retain previous state even after reload on the simulator

I have a code that does something like this:(Pseudo Code)
class extends Component{
constructor(){
this.state={
condition1 : false,
condition2 : false,
condition3 : false,
condition4 : false,
}
//set all 4 condition states to true once data loading has completed from the store
}
render(){
return(
if (this.state.condition1 && this.state.condition2 && this.state.condition3 && this.state.condition4)
Render Main component
else
Render loading screen
);
}
}
Basically I have 4 states that are used to check if data has been loaded before loading the main component.Once the Store (from another file) has finished loading the data, it emits an event,causing the 4 states to be set to true, loading the main component.However, once I hit the reload button from the Genymotion emulator, the 4 states remain as true and does not get reset to false, causing the app to load the main component and crash.(Since the data is not ready yet) I have tried resetting all 4 states to false again in the ComponentWillMount method but it seems like the states doesn't get set back to false in time before the main app attempts to load the main component. In which component of the life cycle should I reset the states to false instead? Or should I ignore this issue since it is "unrealistic" to reload the app on an actual device?
In which component of the life cycle should I reset the states to
false instead?
The docs recommend using componentWillReceiveProps to update the state prior to rendering. I've done this for my projects and it works well, though depending on your use case you may need to check whether newProps is different than this.props.
https://facebook.github.io/react/docs/component-specs.html
componentWillReceiveProps
Invoked when a component is receiving new props. This method is not
called for the initial render.
Use this as an opportunity to react to a prop transition before
render() is called by updating the state using this.setState(). The
old props can be accessed via this.props. Calling this.setState()
within this function will not trigger an additional render.
I have managed to get around this issue by reducing the number of network fetch requests. It seems that having multiple network requests at the same time can cause instability, even though it is unclear why so.

Categories