Why is getDerivedStateFromProps is a static method? - javascript

I am yet to work on static getDerivedStateFromProps so I am trying to understand about it.
I understand React has deprecated componentWillReceiveProps in React v16+ by introducing a new life cycle method called static getDerivedStateFromProps(). Ok but wondering why React has changed to a static method instead of a normal method.
Why
static getDerivedStateFromProps(nextProps, prevState){
}
Why not
getDerivedStateFromProps(nextProps, prevState){
}
I am unable to understand why it’s a static method.

To understand what React is trying to achieve with static methods, you should have a good understanding of the following:
Side-effects
Why is asynchronous code considered a bad approach up until the componentDidMount hook
Asynchronous rendering
How static methods aid in discouraging impure and asynchronous coding
Side-effect is nothing but manipulating any data out of scope. So side-effects in getDerivedStateFromProps would mean changes to any other variable other than its own local variables.
Functions that don't cause side-effects are called pure functions and in the case of their arguments, these are cloned before they are manipulated, thereby preserving the state of the objects that such arguments point to.
These functions simply return modified values from within their scope and the caller can decide the course of action with the returned data.
Inducing custom asynchronous code in a library like React with its own lifecycle flows is not a great idea. It should be carefully inserted at the right moment. Let's understand why by analysing the component creation lifecycle of a custom class component (to keep this short lets consider it is also the root element).
At the beginning the ReactDOM.render method invokes the react.createElement() method call.
react.createElement() => calls new ClassElement(props) constructor => returns the ClassElement instance.
After the constructor call, react.createElement() calls the ClassElement.getDerivedStateFromProps(props) method call.
After the above method returns, react.createElement() calls the instance.render() method.
(this can be skipped)
This is followed up with other synchronous calls such as diffing with
virtual DOM and updating real DOM etc and there are no hooks provided
to tap into these calls(mostly because there is no strong need). A key
point to note here is that javascript execution, real DOM updates and
UI painting - all - happen within a single thread in the browser thus
forcing them to be synchronous. This is one reason why you can write
something synchronous like:
let myDiv = document.getElementbyID("myDiv");
myDiv.style.width = "300px"; // myDiv already holds a reference to the real DOM element
console.log(myDiv.style.width); // the width is already set!
because you know at the end of each of those statements, that the
earlier statement is completed in DOM and in browser window(UI I
mean).
Finally, after the render method returns, react.createElement() calls the componentDidMount to successfully mark the end of lifecycle. Since it's the end, componentDidMount naturally serves as the best junction to attach asynchronous as well as impure functions.
What we must understand is that the lifecycle methods is constantly tweaked for performance and flexibility reasons and is completely under the control of React engineers. It's not just with React, in fact it's with any third party code's flow. So inducing impure functions or asynchronous calls could lead to issues because you would be forcing the React Engineers to be careful with their optimisations.
For e.g. if the React Engineers decide to run getDerivedStateFromProps twice or more times in a single lifecycle flow, both impure functions and asynchronous calls would be fired twice or more, directly affecting some part of the application. However with pure functions this would not be a problem because they only return values and it is upto the React Engineers to decide the course in the multiple getDerivedStateFromProps calls(they can simply discard all the returned values up until the last call and make use of the last one).
Yet another example would be what if the React Engineers decide to make the render call asynchronous. Maybe they would want to club all the render calls (from the parent to all the nested children) and fire them at once asynchronously to improve performance.
Now this would mean that the asynchronous calls written in render method or prior to it(like in constructor or getDerivedStateFromProps) could interfere with the render process because of the unpredictability in the asynchronous process completion. One could complete before or later than the other, triggering their respective callbacks unpredictably. This unpredictability could reflect in the form of multiple rendering, unpredictable state etc.
Importantly, both these ideas aren't just examples, rather were expressed by the React engineers as a possible future optimisation approach. read here: https://stackoverflow.com/a/41612993/923372
In spite of all this, React Engineers know the developers out there could still write asynchronous code or impure functions and to discourage that, they made one of the lifecycle methods static. The constructor, render, getSnapshotBeforeUpdate, componentDidMount and componentDidUpdate methods cant be static because they need to have access to instance properties like this.state, this.props, other custom event handlers etc.(constructor initialises them, render uses them to control the UI logic, other lifecycle methods need these to compare it with earlier states)
However considering getDerivedStateFromProps, this hook is only provided to return an updated clone of the state if the previous props is different from the current props. And by that very definition, this sounds pure with no need for any access to instance properties. Let's analyse why.
For this hook to work, the developer first needs to store the previous props in the instance state(let's say, in the constructor call). This is so because getDerivedStateFromProps receives the instance state as well as new props as arguments. The developer can then proceed to diff the desired property and return an updated clone of the state (without having the need to access this.props or this.state).
By making getDerivedStateFromProps static, not only is React forcing you to write pure functions, it is also making it difficult to write asynchronous calls because you have access to no instance from within this method. Usually the asynchronous call would provide a callback which would most probably be an instance method.
Now this doesn't mean the developers cant write them, instead this is just making it difficult and forcing to keep away from such approaches.
A simple rule of thumb is to stay away from impure and asynchronous functional approaches for the duration of third party induced flows. You should only induce such approaches at the end of such flows.

According to the description of this Proposal:
This proposal is intended to reduce the risk of writing
async-compatible React components.
It does this by removing many <sup>1</sup> of the potential pitfalls in
the current API while retaining important functionality the API
enables. I believe this can be accomplished through a combination of:
Choosing lifecycle method names that have a clearer, more limited purpose.
Making certain lifecycles static to prevent unsafe access of instance properties.
And here
Replace error-prone render phase lifecycle hooks with static methods
to make it easier to write async-compatible React components.
Eventually, after lots of discussions, the goal of using static method is also described officially here:
The goal of this proposal is to reduce the risk of writing
async-compatible React components. I believe that can be accomplished
by removing many1 of the potential pitfalls in the current API while
retaining important functionality the API enables. This can be done
through a combination of:
Choosing lifecycle method names that have a clearer, more limited purpose.
Making certain lifecycles static to prevent unsafe access of instance properties.
It is not possible to detect or prevent all side-effects (eg mutations
of global/shared objects).

You are not supposed to touch any internal data in that method so it is defined as static. This way there is no object you can touch and the only things you’re allowed to do are to use the provided previous state and next props to do whatever you’re doing.

getDerivedStateFromProps exists only to enable a component to update its internal state as a result of changes in props. As we update only state on the bases of props, so there is no reason of comparing nextProps and this.props. Here we should compare only next props and previous state, If state and props are different, update state otherwise there should be no update.
If we compare this.props with next props,we require to store the old props value, which impact performance. Keeping copy of past value is called memoization. To avoid misuse of “this” and memoization, getDerivedStateFromProps is made static.
We can consider above as the reason for componentWillReciveProps depreciation too.

getDerivedStateFromProps is a new API that has been introduced in order for it to be extensible when Async rendering as a feature is released. According to Dan Abramov in a tweet,
This method is chosen to be static to help ensure purity which is
important because it fires during interruptible phase.
The idea to move all unstable things and side effects after the render method. Giving access to component instance variables during an interruptible phase may lead to people using it with all sorts of side effects causing an inconsistency in async rendering

Related

this.watch vs. on Mutate for handling changes in a backdraft watchable

In a backdraftjs component with watchable 'titleString', is there any difference/preference between
this.watch('titleString', this.handleTitleStringChange);
and
onMutateTitleString(newTitle, oldTitle) {
...
}
The onMutate has the benefit that it remembers the old value (oldTitle) but this.watch() is maybe a little easier to find in code since it contains the word titleString -- where for onMutate you have to know to search for the camelcased-and-concatenated version.
Are there any other reasons to use one or the other?
Great question.
To begin...a minor clarification in the question: both methods are provided the new value and old value. See the docs for a watcher.
The main difference is that onMutateproperty-name is a member function. As such, it is unconditionally applied immediately after the actual mutation to the underlying memory slot occurs and before any watchers are applied. In essence, it is an extension of the framework's mutation machinery for a particular property in a particular class that contains the WatchHub mixin. It is used to define/enforce a behavior that is part of the definition of the class. As such, note also onMutateproperty-name can be overridden in subclasses (because, structurally, onMutateproperty-name is a method, and, heuristically, the behavior is part of what defines the mental model of the class).
All that said, it is certainly possible to accomplish most of what onMutateproperty-name does by simply connecting a watcher with the watch instance method...sans subclass override capability, and at the added expense of creating a watch handle and the rest.
On the other hand, connecting a watcher via a component's watch instance method is intended for use by clients of instances of the class. These connections typically should not result in mutating the instance state internally...if that was not true, then a particular instance would behave differently depending upon what clients had connected to its watch interface.
Of course, in JavaScript this is expensive to enforce and the library made the intentional decision to not construct enforcement machinery. This is a general principle of the library: encourage canonical design and implementation paradigms, but don't prevent the using engineer from doing what needs to be done (sometimes the world isn't perfect and we need to do what we need to do).

Why do StencilJS docs recommend making network calls in componentWillLoad?

I'm working on a Stencil JS project, that makes some network calls to get data and update state.
In React JS, a network call would be done in componentDidMount lifecycle method, and not in componentWillMount method.
I'm surprised to find almost the opposite recommendation in the Stencil docs:
componentWillLoad() Called once just after the component is first connected to the DOM. Since this method is only called once, it's a
good place to load data asynchronously.
Stencil favors componentWillLoad over componentDidLoad in another case too. It logs out console warnings when using componentDidLoad to update state:
STENCIL: The state/prop "exampleProp" changed during "componentDidLoad()", this triggers extra re-renders, try to setup on
"componentWillLoad()"
Why does Stencil push users to componentWillLoad method (before render), while React pushes users to componentDidMount method (after render)?
To start off I don't know much about React but I saw that componentWillMount was deprecated with the following explanation.
There is a common misconception that fetching in componentWillMount lets you avoid the first empty rendering state. In practice this was never true because React has always executed render immediately after componentWillMount. If the data is not available by the time componentWillMount fires, the first render will still show a loading state regardless of where you initiate the fetch. This is why moving the fetch to componentDidMount has no perceptible effect in the vast majority of cases.
In Stencil you can return a Promise in componentWillLoad which will prevent the component from rendering until that Promise has resolved (but I never found a practical use-case for this).
If you don't return a Promise then depending on the speed of the your render method (any possibly other render lifecycle methods) componentDidLoad will run slightly later, which will probably be minimal most of the time (i.e. "no perceptible effect") but without any advantage (that I can think of).
componentDidLoad is meant for things that have to run right after the first render.
Similarly the console warning is because most of the time it doesn't make sense to synchronously modify a #State() or #Prop() property in componentDidLoad as it can often be easily moved to an earlier lifecycle method (avoiding a re-render just after the initial render). There are exceptions though, e.g. if the state value depends on the generated DOM, in that case you can ignore the warning.
Note that in some cases connectedCallback might be even more appropriate than componentWillLoad e.g. the clock example in Stencil's Life Cycle Methods docs.

Lots of logic in getDerivedStateFromProps, skinny vs fat gDSFP

I'm wondering if it is bad practice to have 'fat' gDSFP functions. Currently, I have a component that takes in some data and does a bunch of data manipulation
function getDrivedStateFromProps(nextProps, prevState) {
// start doing data manipulation
and along the way if it encounters an error condition it returns a new error slice of state
const someValue = nextProps.something * myFunc()
if (someValue === badThing()) {
return {error: true};
}
// continue doing data manipulation
This repeats several times before it finishes all data manipulation and returns the derivedState that my component needs. I'm curious on what the communities views are on a 'fat' gDSFP functions. My component only runs gDSFPs when the external data source has changed and it needs to derive new state so I don't see where else this logic could live.
I think you may actually be in a situation where using getDerivedStateFromProps might not be the best approach at all. In your question you state...
My component only runs gDSFPs when the external data source has changed and it needs to derive new state so I don't see where else this logic could live.
Given this statement, it sounds like you can use memoization. Here is this concept discussed on the react docs.
The basic idea is that managing getDerivedStateFromProps can get hairy especially when you have lots of logic going on there. If the reason why you want to capture your props in state is only to get a performance boost, then memoization might be your friend.
The idea here is that you do not want some logic to run every time your props change, so what this will buy you is that if you have a function whose arguments are the same as they were before, as an example state has not changed, your function will return the last computed result which it had stored in the cache. This library handles this beautifully and is pretty easy to use.
Another concern people often have which may prompt them to reach for getDerivedStateFromProps is to ensure that their component does not render unless if the props have in fact changed, so by having the component render based off of computed state this can be achieved.
But this too can be achieved without using getDerivedStateFromProps. Using PureComponent can give you this same result with much less fuss. Another option can be shouldComponentUpdate.
In short, unless you have some very specific reason that getDerivedStateFromProps is right for your use case, you may be better off reaching for a solution that involves less hair pulling.

What's the best way to call callbacks passed into a Redux subapp?

I'm following the Redux "subapp" pattern described here, but I'm having a little trouble figuring out the best way to call callbacks passed into the subapp, especially in response to asynchronous actions.
Here's an inelegant but working solution: https://jsfiddle.net/jochakovsky/bgx86m6j/. I'm not a big fan of this approach - it forces me to pass the callback further down than I'd like, into ReduxSubappView, which shouldn't have to know anything about that callback. The most natural place to call the callback is the async action creator initialize, but it does not have access to the props passed into ReduxSubApp. Also, it feels weird to compare props and nextProps - presentational components should really only be responsible for converting the current props into a UI, and not have to understand state transitions. Additionally, ReduxSubappView cannot be a pure functional component with this approach.
What's the best way to handle this type of situation?
If you don't want to pass down props from the Subapp root level and you can't use redux in all Subapps, you would need a global pub/sub or something comparable to subscribe to changes.
Here's a native approach with addEventListener:
// in Subapp:
document.addEventListener('initialized', function(e){console.log('app initialized')});
// Wherever the event originates from:
document.dispatchEvent(new Event('initialized'));
A dedicated library might be better, since this has the events bubbling through the DOM (for no reason really), but it should work out of the box.

React functional component vs. plain-old JS function that returns React element

Suppose I define a simple React functional component like this:
const Greeter1 = ({name}) => <h1>Hello {name}</h1>;
I can also define an equivalent plain-old JS function that returns a React element, like this:
const Greeter2 = name => <h1>Hello {name}</h1>;
The "React" version is of course also just a normal JS function, taking in a JS object instead of a string. We could use either of these functions within plain JS to get the greeter element for a given name, just with slightly different caller syntax:
const greeterElement1 = Greeter1({ name: "Bob" });
const greeterElement2 = Greeter2("Bob");
Within a React expression though, we can call these functions in a few different ways:
const someReact1 = <div>{ Greeter2("Bob") }</div>;
const someReact2 = <div>{ Greeter1({ name: "Bob" }) }</div>;
const someReact3 = <div><Greeter1 name="Bob" /></div>;
My question: Are there any effective differences between these calling styles other than syntax aesthetics? I assume someReact1 and someReact2 are virtually identical, but I'm not sure about someReact3. Does using the React component syntax change the way React treats things? Does it affect behavior or performance in any way? Or is it merely syntactic sugar?
When doing a diff on the virtual DOM tree, does React forgo comparing within a functional component if its attributes haven't changed between renderings? And if so, am I correct to assume that that optimization would be lost when calling functions directly as in someReact1?
I want to know b/c in some cases I actually prefer the style of someReact1 as it allows me to use functional programming techniques like currying more easily, plus sometimes it's nice to not have to specify parameter names when calling. But am I paying some kind of penalty by doing so? Am I better off sticking with the traditional syntax?
Component functions are (a bit?) faster than class components since React 16. Not sure if it's true for prior versions.
Calling component function as a function is much faster than calling it via JSX/React.createElement.
But, in most cases, this should not affect the way we write our code since JSX is pretty readable. To fill this gap we should use #babel/plugin-transform-react-inline-elements.
However, I understand your passion to call them as functions. For the sake of composition, I also find it quite useful. This is even more true for plain functions.
But, again, in the future, functional components are going to have a state. If it happens, we may regret our passion :)
Plain functions seem to be faster than component functions. However, I have not digged this topic yet. For example, I'm unsure if it works OK with react-hot-reload. Or, its pros/cons for performance and possible optimizations.
IMO, no matter the differences and in any case, optimizations are still on us - be it a shouldComponentUpdate, or memoization, or keys. Components which might not need any optimizations should be cheap to re-render, and vice versa (if it is a pure calculation, not DOM operation, obviously).
Feel free to comment or correct, I'll update my post.
I found an explanation, apparently someone asked a similar question in a Github issue on the official React repository. Here's a link to the issue https://github.com/facebook/react/issues/16796 and an excerpt from the thread, an answer provided by a contributor:
What I want to understand is this: Is it actually necessary to call
React.createElement again, since it is already in the return value of
the component? Is there any effective difference between that and
calling the function component directly, like this?
As #hamlim mentioned, the effective difference is that simply calling a component as a function wouldn't work if the component was more complex than a pure function that returned another React element. React needs the element returned from React.createElement to handle those features.
In the HelloMessage example you could technically just call it as a function, which would be equivalent to doing:
ReactDOM.render(
<div>Hello Taylor</div>,
document.getElementById("hello-example")
);
This just inlines the result of HelloMessage, which would render the same UI in the browser. Thats effectively what you're doing by calling HelloMessage as a function. Internally this would be different to React since there's no component mounted, but practically speaking the behavior is identical in this trivial example.
For the ParentComponent example, the same constraints apply. You could call the ChildComponent components as functions if they were just simple components without state or effects, and that would be the same result as just inlining their content into ParentComponent. In same cases this might be what you want, but typically not. If someone added state or effects to one of the ChildComponents, or wrapped it in React.memo or React.lazy, this would break. So use this pattern with caution.

Categories