If you do an asynchronous action that updates the state in componentWillMount (like the docs say), but the component is unmounted (the user navigates away) before that async call is complete, you end up with the async callback trying to set the state on a now unmounted component, and an
"Invariant Violation: replaceState(...): Can only update a mounted or
mounting component."
error.
What's the best way around this?
Thanks.
You can use component.isMounted method to check if component was actually attached to the DOM before replacing its state. Docs.
isMounted() returns true if the component is rendered into the DOM,
false otherwise. You can use this method to guard asynchronous calls
to setState() or forceUpdate().
UPD: Before you downvote. This answer was given 2 freaking years ago. And it was the way to do stuff back that days. If you are just starting to use React do not follow this answer. Use componentDidMount or whatever another lifecycle hook you need.
isMounted() is actually an easy way to solve most problems, however, I don't think this is an ideal solution for concurrency issues.
Now imagine that the user clicks very fastly on many buttons, or maybe he has a very poor mobile connection.
It may happen that finally 2 concurrent requests are pending, and on completion will update the state.
If you fire request 1 and then request 2, then you would expect the result of request 2 to be added to your state.
Now imagine for some reason request 2 ends up before request 1, this will probably make your app unconsistent because it will show request2 results and then request 1, while your last "interest" was actually in request 1 answer.
To solve this kind of issue, you should rather use some kind of Compare And Swap algorithm. Basically, this means before issuing the request, you put some object node in state, and on request completion, you compare with reference equality if the node to swap is still be node you are interested in when the request completes.
Something like this:
var self = this;
var resultNode = {};
this.setState({result: resultNode});
this.getResult().then(function(someResult) {
if ( self.state.result === resultNode ) {
self.setState({result: someResult})
}
}):
With something like that, you will not have concurrency issue in case the user clicks to fast on the buttons leading to current requests inside the same component.
update 2016
Don't start using isMounted because it will be removed from React, see the docs.
Probably the best solution for problems arising from async call from cmomponentWillMount is to move things to componentDidMount.
More info about how to properly get around this problem and how not to need to use isMounted here: isMounted is an Antipattern
Related
When I learn React, everyone tells me setState is async because if I console.log(state) right after setState, it will print the old value.
However, when I check the source code, useState doesn't return any promise nor use async/await.
Meaning setState must be sync. But if it's sync then why React doesn't re-render the component right away when I call setState? As you can see in the code snippet below, 0 gets printed first, then render1 is printed later.
If this is because of batching then how does batching work? How is it possible to "wait" until specific time to trigger the re-render of App component. And even more important, how does React know that it needs to call App in this case and not other functions?
export default function App(){
const [count, setCount] = useState(0);
console.log("render", count);
return <button onClick={() => {
setCount(count+1); // why doesn't App get called/re-rendered right away here
console.log(count); // this prints the old value first then re-render happens later
}}>
</button>
}
First, I would caution you that the reason why console.log(count) logs out the old value has nothing to do with sync vs async. The reason why you log out the old value is that count is a local const which will never change. It started its existence as the old value, and it will always be the old value no matter how much time passes, and no matter the order of operations.
Calling setCount does not change the value in count, it just asks react to rerender the component. When that new render happens, a new set of local variables will be created, with the new values. But your console.log from the previous render can't interact with values in the next render.
That said, it is true that rendering in react is (usually) delayed briefly to batch up multiple changes. The exact way they do this i'm not sure, because there's several possibilities. I've previously looked through their code, but enough has changed in the last few years that i doubt what i found will still be useful. In any event, ways that they could batch the rerender include:
setTimeout. The first time you set a state, they could set a timeout to go off soon. If another set state happens, they take note of the state, but don't bother setting an additional timeout. A little later, the timeout will go off and the rendering happens.
A microtask. The easiest way to do this is Promise.resolve().then(/* insert code here */). Once the current call stack finishes executing, microtasks will run. This approach has the benefit that it can happen sooner than a setTimeout, since timeouts have a minimum duration
If execution began inside react, they could just wait until your code returns, then do the render. This used to be the main way react batched changes. Eg, they had a piece of code responsible for calling your useEffects. During that, it would take note of state changes, but not rerender yet. Eventually you return, and since returning puts code execution back in react's hands, it then checks which states have changed and rerenders if needed.
how does React know that it needs to call App in this case and not other functions?
It knows that because the state that you changed is a state of App. React knows the tree structure of your components, so it knows it only needs to render the component where you set state, plus its children.
I'm looking for information about whether promises chains are guaranteed to be completed before being garbage collected if the reference is lost. I'm executing a call to my API within a React useEffect hook, but am not clear about what happens if one of the dependencies change and the useEffect hook is re-executed while the promise is still pending.
A contrived example:
const useApiFetch = (query, myVariable) => {
const client = useClient();
useEffect(()=>{
client
.query(query)
.then(() => console.log('inside thenable'))
.catch(() => console.log('inside catch');
},[variable, client]);
}
Questions:
What happens if myVariable changes and useEffect is re-executed while the promise is pending? Will the promise complete before being garbage collected?
What about if the component that is consuming this hook is re-rendered or removed from the virtual DOM?
If I don't have a cleanup function, is there any chance of a memory leak?
I may not be the perfect one but from my usage with react and functional components i will try to answer these.
When your dependencies for the hook changes, the function inside is re invoked. That means in your case a second api call. What this in turn results in may depend on api response time, client device, internet speed and way you handle these.
For example, if you are rendering something in the promise success for example a Text. What happens is once you UI renders the first data that it gets, like if you are setting state or something. Aa soon as the next api call resolves the UI again re-renders to reflect that change.
taking your example code. If you change the dependency "variable" three times, you get three "inside thenable" if promise resolves or else the catch console.log.
Note: If for any reason you api process takes some time for example a large query, then you may not be able to tell which api call will get resolved first. I had this issue when i implemented a text based onChange search using api. A good solution will be to debounce your api calls to limit no of calls and also to cancel unwanted calls.
If you have setStates that is tied to your promise resolution/rejection and you haven't properly handled the unmount condition. React will show you a warning stating the same. Something that means "You have a state change happening in an unmounted component/screen". This can lead to memory leaks if left unchecked.
Hope you get some point out of this. I am not a pro at this, these are somethings that i found while working with React.
I would like to know how long it takes my app to become "ready" for the user to interact with it. The timeline of the app loading includes the following:
DOM load.
Initial React render.
HTTP calls triggered from various componentDidMount()s
HTTP calls return. Redux state is updated with HTTP responses.
React renders with new values from HTTP responses.
All initial loading is complete. The user is ready to interact with the app.
At the end of this process, I'd like to fire a tracking event to record the value of window.performance.now().
I'm not sure what the best way to do this is. React's component system does a good job of decoupling various parts of the UI. In this case, it, unfortunately, means that I'm not going to have one easy place to check to know "has everything been rendered with the new data".
Things I've tried or considered:
Look for a React lifecycle hook that tells me if any child is updating. Something like componentOrAnyChildDidUpdate(). I don't think this exists, and it may be counter to the React philosophy.
Hack together an anyChildDidUpdate() lifecycle hook via context and making every component in my app either subclass a helper abstract base class or be wrapped in a higher-order component. This seems bad because context is an experimental API.
Subscribe to the Redux state via store.subscribe(). Update the Redux state to have an explicit record of whether all the HTTP calls have returned. Once all the HTTP calls have returned, and React is finished re-rendering, then I know to fire the tracking callback. The problem is knowing when React is finished re-rendering. It would work if my store.subscribe() the callback was guaranteed to be executed synchronously after react-redux's callback. But that is not the case.
Is there a good way to do this in React?
I'm afraid there is not a universally good way to do this in React, this is "logic" that is related with the structure of your application.
I wanted to display a loader when navigating from one page to another in Single Page Application(SPA) written in react and I faced a similar problem not knowing when to stop displaying it and if all the components in the new page have completed their API calls/renders.
The way I resolved it was:
1) I removed all my API calls from inside my components.
2) Create a Page component to wrap all the components included in that page(invoked by your react router).
3) Perform all requests required for your components simultaneously on navigation(in my case while displaying the loader). For each request that completes, create a promise and inside it create your component dynamically using React.createElement. Pass to the component created the request response and the promise handler as props.
4) Resolve the promise in your component's componentDidMount.
5) Once all the promises are resolved you know the "page" is ready and you can record the value of window.performance.now().
It is hard to provide a minimal code example without a lot of out of context code since this code is spread across the application.
There's more than one way to do what you're asking - but off the top of my head I would do the following:
• Create a perf reducer and call performance.now() right before I make the redux store and add the value to the initial state.
{ perf: { start: '...', end: '...' } }
• Track loading status of initial HTTP requests in a loaded reducer.
{ loaded: { request1: false, request2: false, request3: false } }
• Connect top level component to loaded reducer and check if all requests are complete in componentDidUpdate. If true add end value to perf reducer.
import React from 'react';
import { connect } from 'react-redux';
class App extends React.Component {
componentDidUpdate(prevProps) {
if (!prevProps.loadingComplete && this.props.loadingComplete) {
this.props.updatePerf(performance.now());
}
}
}
const mapStateToProps = state => ({
loadingComplete: state.loaded.every(item => item),
});
const mapDispatchToProps = dispatch => ({
updatePerf(time) {
dispatch({ type: 'SET_ENDING_PERF', payload: time });
},
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
In our project, we use redux and a couple of actions - trigger, request, success, error.
"Trigger" calls request and throw loading: true into our "smart" component
"Request" fetch data and throw them into Success/Error
"Success", tells us that everything is fine and data loaded and throw { loading: false, data }
On success/error, we always know that is happening with our application
And of course you can use componentWillReceiveProps(nextProps) {} and check loading flag and data in your component
You can try to use react-addons-perf
That's how I benchmark my apps usually:
Basically you want to start measuring when your component starts updating and stop the measurement after the lifecycle method componentDidUpdate().
import Perf from 'react-addons-perf'
componentWillMount() {
window.performance.mark('My app');
}
componentDidMount() {
console.log(window.performance.now('My app'));
Perf.start()
// ... do your HTTP requests
}
componentDidUpdate() {
Perf.stop();
Perf.printInclusive();
}
Like that you will track your component initial render + your component updates.
Hope it helps
Two steps to achieve this.
Track all HTTP-requests via actions
Simple counter in redux state would be enough, that increments on start of request and decrements on success or fail (all via appropriate actions).
No window.dirty.stuff here in front of Dan Abramov.
Track all connected components
componentDidUpdate method indicates that all children finished rendering. This can be achieved without much boilerplate with recompose's lifecycle HOC. Right place to start tracking re-render might be sCU method or just cWRP if you use recomposes shouldUpdate or reselect already - since we won't track components that didn't update.
Note that all is assuming idiomatic react/redux app, no tricky side-effects or animations. For example, if some child component somewhere deep in the hierarchy fires its own AJAX request, say on cDM, not via redux action. This way you have to track them too, BUT no one can be sure if they imply any re-renders.
Tracking such behaviors is still possible, just might require more effort.
from a design POV, in my opinion being ready or not is a user presentation property and so should be handled by react components alone.
for example, you may later decide to allow the user to interact with some part of your UI while some other parts are still loading, or you may decide that some components should allow the user to interact before the data model is ready, etc ... in other words, it's a (react) component responsability to tell if/when it's ready or not and nobody else.
That is, all 'waitable' components could expose an onLoad-like prop to be passed up the react tree; the root component would then coordinate the result by changing the tree down stream accordingly by propagating the appropriate props down to leaves.
This could be implemented by writing a mixin/high order component to incapsulate the "readyness update" logic.
I'd like to run a function upon receiving props initially and upon any subsequent props changes. Consequently, I was planning on checking props and running this function in both componentDidMount and componentWillReceiveProps. However, this seems redundant. I've seen people also check props and run functions inside render(), but I was under the impression this was frowned upon.
What should be my solution here?
There is no substitute to catch both componentDidMount and componentWillReceiveProps in a single callback. The best way to do this is to define a function and call it from both of these callbacks.
I was planning on checking props and running this function
Is a very vague description of what you're trying to do. Just checking the props alone doesn't seem to be problematic if done inside the render function. But it would become a problem if this part is causing side effects which (can) in turn trigger a component re-render. If this is the case, I'd say "frowned upon" is putting it lightly. The problem with this is that your render function is adding side effects which are in turn triggering more re-renders and on it goes. It will instantly kill the performance of your app and could even make the whole thing grind to a halt.
Your question is tagged with both React and Redux. Are you using a mapStateToProps function that you use with connect?
If so, a possible answer could be to dispatch this action (or run this function) in the mapStateToProps function, since this will be run both initially, and then every time you update the redux state. You'd have to make sure the action/function doesn't cause yet another state change though, so that you end up in a loop.
Edit: #DuncanThacker pointed out this might not be a good idea, because mapStateToProps may fire very often, resulting in performance issues. Good point.
The general problem: Let's say I have a button with an onClick handler calling an action creator. The action does an ajax call which dispatches a message when ajax responds, and this in some way affects the UI. Given this basic pattern there's nothing stopping the user from clicking this button multiple times, and thus running the ajax call multiple times.
This is something that doesn't seem to be touched upon in the React or Flux documentation (as far as I have seen), so I've tried to come up with some methods on my own.
Here are those methods
Use lodash.throttle on a method which does an ajax call so that multiple clicks in quick succession don't create multiple calls.
Use lodash.debounce on a method so that ajax is only called once a user hasn't done any activity for a bit. This is how I'm doing semi-realtime updates of text fields on change.
Dispatch an "is updating" message to stores when the action is first called and then dispatch a "done" message when the ajax call returns. Do stuff like disabling input on the initial message and then re-enable on the second.
The third method seems to be the best in terms of functionality since it allows you to make the user interface reflect exactly what's going on, but it's also incredibly verbose. It clutters absolutely everything up with tons of extra state, handler methods, etc...
I don't feel like any of these methods are really idiomatic. What is?
Hal is pretty much correct. Dispatching multiple messages is the Fluxiest way to go.
However, I would be wary of dispatching an IS_UPDATING message. This makes reasoning about your code harder because for each AJAX action you're dispatching several actions at once.
The idiomatic solution is to split your AJAX "actions" (action-creator-actions) into three dispatched actions: MY_ACTION, MY_ACTION_SUCCESS, MY_ACTION_FAILURE, handling each instance appropriately, and tracking "pending-ness" along the way.
For example:
// MyActionCreator.js
// because this is in a closure, you can even use the promise
// or whatever you want as a sort of "ID" to handle multiple
// requests at one time.
postMessage() {
dispatch('POST_MESSAGE', { ... } );
api.slowMessagePostingAjaxThingy().then(
(success) => { dispatch('POST_MESSAGE_SUCCESS', { ... }); },
(failure) => { dispatch('POST_MESSAGE_FAILURE', { ... }); }
);
}
// MyStore.js
on('POST_MESSAGE', (payload) => { /* do stuff */ });
on('POST_MESSAGE_SUCCESS', (payload) => { /* handle success */ });
on('POST_MESSAGE_FAILURE', (payload) => { /* handle failure */ });
This gives you several benefits over your alternate solutions:
Your store is exclusively in control of whether an item is pending or not. You don't have to worry about changing UI state on actions in your UI code: you can have your UI look exclusively to a pending property of your store for truth. This is probably the biggest reason for using Flux over MVC systems.
You have a clean interface for taking your actions. It's easy to reason about and easy to attach other stores to this data (if you have a LatestMessageStore or something, it's easy to subscribe to these events). This is the benefit over using IS_UPDATING as Hal suggested.
You save your lodash calls for when they semantically make sense— like when you may be inundated with legitimate data (a text field).
You can easily switch between optimistic updates (change the store when POST_MESSAGE is called) or pessimistic updates (change the store on POST_MESSAGE_SUCCESS).
I would argue that the third method is the correct way, but I don't find it to be verbose. A lot of React code that I see written sort of misses the spirit of React with its idea of very small, composable components. When large monolithic components are created, yes, things can get very messy.
But if the button in question is its own component, then it can take care of rendering based on its state. When a user clicks the button, the state of just that component changes -- and it renders it in a way that it can't be clicked again.
Once the store has notified that component that it has changed, the component can set its state back -- and with it, re-render itself.
It's a pretty straight-forward process; it just requires thinking about pages as a collection of small, composable units.