Description
Hi. This seems like a new case for #8 , except that one was closed based on side effects in render
. Here we have a valid side-effect in componentDidUpdate
that gets triggered unexpectedly.
Essentially, we have a
loadRootEntityAsync({ rootEntityId }) {...}
render:
<Async
promiseFn={this.loadRootEntityAsync}
rootEntityId={rootEntityId}
>
<Async.Fulfilled>
{rootEntity => (
<RootEntity
rootEntity={rootEntity}
selectedChildEntityId={selectedChildEntityId}
/>
)}
</Async.Fulfilled>
</Async>
Note that selectedChildEntityId
can be null, when the user doesn't navigate using a precise URL, but then there's some logic in <RootEntity>
that will select the appropriate child entity - this logic happens in <RootEntity>
's componentDidUpdate
.
So, it works perfect on the show of the first root entity, but when user navigates to another root entity (by changing rootEntityId
and setting selectedChildEntityId
to null
), the old <Async.Fulfilled>
, just before the <Async.Pending>
for the new fetch, gets another invocation with the old rootEntity
, and empty selectedChildEntityId
, which triggers a new finding of selectedChildEntityId
, resetting the user navigation. This is the usual shape of the problem, but if the user navigates straight to a specific selectedChildEntityId
(e.g. using browser back button or using a direct link) it will result in creating a component tree where the new child entity is in the context of an old root entity. This, will throw an error in the best case, but might as well cause corruption, depending on what the components do in their lifecycle methods.
For now, the workaround is to use <Async key={rootEntityId}>
, a common technique for preventing components reuse, which prevents that last unnecessary firing of the <Async.Fulfilled>
's function.
It seems though this could be prevented elegantly by preventing the render of the <Async.Fulfilled>
's function the moment the arguments to promiseFn
change. Instead of that one, <Async.Pending>
could be used in this render
, which, semantically, seems to be even more correct - another execution of the async action is imminent.