(React) Run method on props change, including initial props - javascript

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.

Related

Is setState really asynchronous?

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.

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.

Why does a react component render twice?

I've placed debuggers in the render of a component and see that it gets ran twice sometimes, if not most of the time. Is it because the first render is before the component receives any props? And the 2nd time is when it receives it? I thought components go through the lifestyle of mounting, receiving props, and then rendering? Or does it mount, render, check props, and then re-render?
Components do not re-render if they have initial props. The only reason that it would re-render is if it is receiving props after the initial render, or if you are changing state.
In my previous question, I had an issue, and I quote:
" ...I know that most of these features (and possibly more) are available in function components (introduced most by hooks), but they do not work as I intend them too, because they are NOT exactly the same, like useEffect(() => {code}, []) is known to replace componentDidMount(), an yet upon mount it renders twice in any app I develop, passing false data... "
To that issue, CertainPerformance replied:
" ...Sounds like you have strict mode enabled and are performing operations with side-effects, which should be avoided. You probably just need to figure out the right way to structure the code with functional components - nearly anything you'd want to do can be done reasonably elegantly in functional components, though not absolutely everything... "
Which lead me to research a little and find this interesting answer:
It's an intentional feature of the StrictMode. This only happens in development, and helps find accidental side effects put into the render phase. We only do this for components with Hooks because those are more likely to accidentally have side effects in the wrong place.
You can read more about that on GitHub.

Why should addChangeListener be in componentDidMount instead of componentWillMount?

I saw this line as an answer to another question on here:
"componentWillMount should be componentDidMount, or else you'll leak event emitters in node."
and I don't really understand it. Can someone explain with more detail?
More info:
Building a react application with flux, as part of the initial render, a child component computes some data. Ideally, after this data is computed, I would like to call an action that updates the store's state with a portion of this new data.
Normally, updating the store's state emits a change event that causes a re-render. However, because the change listener isn't being added until componentDidMount (rather than in componentWillMount), my top level component isn't able to listen for the change that occurs during the initial render and initiate a re-render.
If I move the addChangeListener to componentWillMount that would seem to fix this issue, but the above quote suggests that this is a bad idea?
I think the prevailing wisdom that listeners should be set in componentDidMount because it prevents problems in isomorphic applications is a mistake. I think in 98% of cases for non-isomorphic applications setting listeners in either componentWillMount and componentDidMount will work the same way, but it is conceptually wrong and in the 2% of cases (such as the example given in the original question) it will do the wrong thing.
There are git issue discussions and comments in the React source code suggesting that it would be preferred that componentWillMount wasn't called on the server at all, but if it isn't then problems are created in the checksum test comparing the server prerender to the client initial render. Having componentWillMount on the server means that it isn't executed as part of the component lifecycle in this case, but this is being used as an excuse to not count it as part of the lifecycle in any case.
In fact, componentWillMount is exactly the right place to register listeners if you are not creating an isomorphic application. If you are creating an isomorphic application then you have to make some compromises due to the checksum/lifecycle issue not being ideal in this case (maybe just testing for the server environment and then not registering listeners?).
In non-isomorphic applications adding listeners in componentWillMount can save unnecessary re-renders in some cases and will register them in document order. The advantage of document order is that if you have a way to flush pending events as components are re-rendered (for example, takeRecords on a MutationObserver) then you can ensure the document is re-rendered top down instead of bottom up, converting the rendering complexity to linear from polynomial.
Additionally, there is no danger period between the initial render and when the listener is registered where the Store can change without triggering a render, causing the view to be out of sync with the Store (the example problem given in the original question). If the listener is registered in componentDidMount you either need to make sure the Store isn't changed in componentDidMount calls in children, or else force a re-render/re-sync after registering the listener, which if done in componentDidMount is done in reverse document order which may be polynomial complexity (depending on how/if the React setStates are aggregated).
Is hard to understand what that quote means without more context. What I can tell you is that there are huge differences between the two of those methods.
On one hand, componentWillMount is called before the component is actually added to the DOM. This is the last chance you have to update component's state and get it rendered before the component is rendered by the browser.
On the other hand, componentDidMount is called once the component has been attached to the DOM (the real one).
What you need really depends on your use case. In general, componentDidMount is used to integrate with other libraries (like jQuery), it provides a way to modify the HTML rendered by the component.
I suggest you to read these links:
https://facebook.github.io/react/docs/component-specs.html#mounting-componentwillmount
https://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount
https://facebook.github.io/react/tips/use-react-with-other-libraries.html

Categories