Is ResizeObserver API necessary or preferred in React? - reactjs

We can already detect dimension change by using ref and useEffect in React:
useEffect(() => {
console.log("height changed to", ref.current?.clientHeight)
}, [ref.current?.clientHeight])
I wonder what is the advantage of ResizeObserver over this ref + useEffect approach? I'm surprised there's no online discussion around this, given ResizeObserver has been there for awhile since 2018. I can see if you're using Vanilla Javascript then ResizeObserver might be the best way to get dynamic height.
But in React I just don't understand how ResizeObserver API is better. I tested both and in Chrome both fire updates almost at the same time. I researched online and many are talking about how to use React Hook with ResizeObserver, but rarely do these posts talk about ref + useEffect already suffice.
Does anyone know if using React, any advantage using ResizeObserver API, or it really doesn't differ much from ref + useEffect approach?

We can already detect dimension change by using ref and useEffect in React:
For a useEffect to run, the component must be rendering. For the component to be rendering, something somewhere must have set state. So if all of that is happening, then yes, your effect can notice the size has changed since the previous render.
But if nothing sets state, then none of that will happen. Maybe you're getting lucky, and some component happens to be setting state around the time the size changes, but that's not something you should rely on. If you delete some random component, or try to optimize performance by skipping rendering, you can suddenly break your resize "detection" code.
The point of the resize observer is to get notified that the change has happened. Then you can set state, and cause the rerender. So now instead of hoping a rerender happens, you know it will happen.

Related

What does it mean by useState are asycnhronous in react

I found some old post that a guy having problem with his code in React and alot answer said that it was caused by because useState is asycnhronous. I was curious what it mean by asycnhronous and I found this article
useState is an asynchronous hook and it doesn't change the state
immediately, it has to wait for the component to re-render. useRef is
a synchronous hook that updates the state immediately and persists its
value through the component's lifecycle, but it doesn't trigger a
re-render
But i still don't understand what it means and why is such a problem. Can someone give me an example?
I would say this is one of the myth to call useState asynchronous.
IMHO, if you open the source code of react, I'm speaking to react 16.x and below (because i didn't read after 16), useState isn't asynchronous.
So why are we saying useState behaves as asynchronous code? We need some coding example.
const App = () => {
const [value, setValue] = useState(1)
const onClick = () => {
setValue(2)
console.log(value)
}
return <input value={value} />
}
In the above typical setup of useState, when you call setValue(2), value wouldn't become 2 right away. This is apparent if you put a console.log after that. Then why the input does show 2 instead of 1, you might ask?
That's because React made a second render to App, you can think of App has been called one more time after setValue. For this reason, people call useState async.
But then why I'm not calling useState async? well, this is a bit long story. The short story is that useState is implemented inside React using plain non-async codes, therefore you can't say a code async just because it has been called twice or it has been deferred to call. Async means a bit differently in Javascript. This is another topic which I won't get into here.
I don't want to speak for the React team as to why state changes are asynchronous, but here's some random dude's explanation. I'll be referring to the JavaScript event loop, which is basically a program that runs many times per second. Each time it runs, it processes a series of tasks synchronously. Asynchronous code is executed across several event loops. This is a very basic and oversimplified explanation - you should go read more about the event loop.
So your initial state declaration happens in your initial render - the first time the function is called. This initial render happens synchronously - meaning the function is executed top to bottom in a single event loop. It's worth pointing out that React components cannot be async:
const SomeComponent = () => {
const [foo, setFoo] = useState("bar");
return <div>The value of foo is "{bar}"</div>;
}
After your initial render, the state only changes when something happens: user clicks a button, the screen resizes, a timer interval fires, etc. Whenever an "event" happens, there can be a lot of processing going on in the JavaScript event loop.
Let's look at a button click, the event propagates all the way down and back up the entire DOM tree which contains the button. The browser is also rendering different states of the button, causing the browser to repaint. You also have developer code which is running in the button click handler which might be doing some heavy lifting like iterating over an array. This all happens within a single event loop. The point of this explanation is to convey that "a lot is going on". Eventually, the state will get updated with setFoo(...).
At this point, React now has to figure out which component changed, calculate and rerender your application in a virtual DOM, perform a diff with the actual DOM, and then apply any changes to the actual DOM. Only at this point does the browser finally paint the result to the screen. This whole operation can be very taxing on a CPU.
If react were to handle state changes synchronously, all of this virtual DOM diffing and calculating would happen in the same event loop as the initial button click. It is very likely that most apps would cause the browser to sporadically freeze and jitter because the CPU is doing so much work at one time.
There are also some other things to consider. Suppose the window resizes and a bunch of different components perform some calculations and update their state. You don't want react to synchronously rerender every time a component changes its state. You want react to kind of wait and collect a whole bunch of state changes and process them at the same time. Browsers also provide mechanisms like requestAnimationFrame to help web-based applications asynchronously render in such a way that works best for the user and their system/browser resources.
There are many other things to consider, but this post is getting long. Hope this helps.

What's the idea behind useState functionality in ReactJS?

I am learning ReactJS and I understand the benefits of functional programming. However, I am a little concerned about the actual useState approach. If we assume a variable to be changed, then why declaring it const in the first place?
I see that I can simply use let number = 4; and then render it like this <p>{number}</p>. What I cannot do however is to re-render it automatically just by changing it, for example using onClick event like this <p onClick={() => ++number }></p>. Why is it that so? Is there a specific reason I am missing why it was implemented the way it is? I mean why the developers have decided that if the value needs to be re-rendered upon change, then it must be a const value declared with the help of useState functionality?
I am asking this because I am suspecting I am missing some good points behind this and I would like to understand them.
The variable is declared as a const because you are not allowed to set it directly. In React the state itself is immutable. You are just allowed to change it over setState (or setNumber in your case) or with actions if you use redux.
But why is that? It may seem unnecessary cumbersome in the beginning
First of all, if your variable number changes, react has to trigger a rerender.
If the state is mutable, it requires data-binding because if the number is set, it has to update the view.
In javascript, data-binding works for simple objects, but not well for arrays. Vue.js for example, as an alternative that uses two-way data binding, had a lot of trouble in its early versions when dealing with arrays. That's why there are now only seven predefined methods to interact with arrays (which they added later to solve that problem). Vue Js Array Methods
So a simple reason to declare the state as const is that it works better with arrays. And if you watch the example you gave, setNumber(number + 1) is not that much more to write than number++. But setState(newArray) works, and newArray[i] = newElement would not work, because due to javascript limitations this cannot trigger a rerender.
Secondly, it is a nicer design concept. Think of your component as a function, that returns a view to a state. And if the state changes, you get a new view. It simplifies relationships between properties in your component. If you were allowed to change your state while rendering your component, it would create a mess.
The problem is that you're thinking of a functional component as if it was stateful. But it isn't. It's a function and once you run it, that's it.
Take this example:
function useState() {
let value = 1
function setValue(v) {
value = v
}
return [value, setValue]
}
function myFunction () {
const [value, setValue] = useState(); // <----- we use const here
return null
}
Even though we're using const, the value variable only exists within the function, once the function returns that's it. It's the same for components.
The actual value of value is stored in a whole different scope, where useEffect has access to.
Here's a deep dive on how react works internally if you're interested
React works in render cycles, i.e. some state is declared, the DOM (UI) is computed during the "render phase", and then flushed to the actual DOM during the "commit phase".
Within each cycle state is considered constant (const in JS simply means a variable can't be assigned a new value, you could just as easily declare it with let or var instead and react would work the same) but for react's purpose, state is constant during a render cycle. When it is updated via one of the state update methods, react then kicks off another render cycle (update state, compute diff, commit to DOM) and re-renders when necessary.
This process is important and the reason why external state mutations are considered anti-pattern, it goes against the react workflow and leads to buggy code or worse, UI that doesn't update as expected.
React component lifecycle
I cannot do however is to re-render it automatically just by changing
it, for example using onClick event like this <p onClick={() => ++number }></p>. Why is it that so?
React state updates use a process called reconciliation to figure out what changed and what to re-render. In really simplistic terms, when react state is updated it is updated with a new object reference so a shallow object comparison can more quickly detect that the component state updated.
Declaring state and doing ++number simply changes the value but not the reference, and would be considered a state mutation, an anti-pattern in react.

useEffect or useMemo for API functions?

which is the best hook for dispatching API calls in a component. Usually I use useMemo for calling the API on the first render, and useEffect if I need extra side effects, is this correct? Becouse sometimes I get the following error:
'''index.js:1 Warning: Cannot update a component (Inscriptions) while rendering a different component (PaySummary). To locate the bad setState() call inside PaySummary, follow the stack trace as described in ...''''
That happens when I route to a component and rapidly change to another one, it doesn't "affect" the general behaivour becouse if i go back to the previous component it renders as expected correctly. So how should I do it?
Calling an API is a side effect and you should be using useEffect, not useMemo
Per the React docs for useEffect:
Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects. Whether or not you’re used to calling these operations “side effects” (or just “effects”), you’ve likely performed them in your components before.
Per the React docs for useMemo:
Remember that the function passed to useMemo runs during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in useEffect, not useMemo.
Performing those side effects (and modifying state) during rendering or with useMemo is the reason you encounter the errors you mention.
basically I rather to use useEffect in componentDidMount manner, with no dependency like below
useEffect(() => {
// Api call , or redux async action here...
}, [])
for calling api's at component mount state.
most of the time i find my self using useMemo for memoising the data at functional Component render level, for preventing the variable re-creation and persist the created data between renders except the dependency changes.
but for the context of your question, there is a hook called useLayoutEffect which is primarily used for actions to happen before painting the DOM, but as i said basically most of the time in projects i find calling apis in a simple useEffect with no dependencies aka, the did mount of your component, in order to load the required data for component!
A bit late but, while everything mentioned above is completely true; the error
'''index.js:1 Warning: Cannot update a component (Inscriptions) while rendering a different component (PaySummary). To locate the bad setState() call inside PaySummary, follow the stack trace as described in ...''''
Has to do with the fact that the API call is Asynchronous and when you rapidly change the pages, the set state call (for updating the data returned from the API call I assume) is still waiting to be called after the data is returned from the API. So, you have to always clean up your Async functions in useEffect to avoid this error.

What is the use case of useMemo in React?

I need help to understand when to use useMemo hook in React vs useState + useEffect. From what I've understood after reading other questions on S.O. and websites :
With useState you can create a value (changeable through the setter) and you can use useEffect to do whatever you need if some of the dependencies listed in the dependency array changes. On first render for example your component will be rendered with the initial value passed to useState, then the effect will run and your component will re-render with the new value you set with the setter in your effect.
With useMemo you can create a value that will be computed from a function you give to the hook and it will change if some of the dependencies in the dependency array changes. So far, to me, it looks like useState + useEffect except you don't have the setter to change the value. On render your useMemo will run if one of the dependencies has changed. Difference here : your component will wait for the value to be computed before rendering (not sure about this part).
From what I've read, useMemo should be used for heavy operations. But if it's blocks the rendering of the component what's the point ?
Some says that with the dependency array you can avoid unnecessary updates but I can also do it with useEffect without blocking the render ? If I don't have a heavy component to render, isn't it faster to use useEffect ?
Thank you :)
Edit :
I accepted Philip Feldmann's answer as it fits my needs. As suggested in the comments you can take a look at this question for a global comparison :)
You are correct, useEffect runs after rendering as a side effect. That means that when you don't want to block the render loop, you can use your version.
That version will however trigger another rerender through calling the setter.
The initial time to paint might be faster, but you'll instantly block the render loop again by setting the new value, which in most cases should be slower than just using useMemo directly.

React state update while rendering

I need to update a state in my react app while rendering the content. Is there any option to update state while rendering.
Note: I am very new to react
You can use componentDidUpdate() Lifecycle method.
and inside componentDidUpdate() function update your state.
FYI: componentDidUpdate() calls every time if component gets updtated.
https://reactjs.org/docs/react-component.html
Futureproofing Note: answer relevant to v16.4.
The answer to your question depends on what you mean by saying "while rendering".
When react invokes the render method of a component, it does not really render anything, yet. The results of the render method will undergo "reconciliation", which means that it will be compared to what's rendered at the moment. If a difference is detected, react will actually re-render the DOM, as effectively as it can.
This means that you can not do anything when react is actually rendering / re-rendering the DOM, since your code is not being executed.
You can, however, do something after the render method has been invoked.
As others have stated before me, the componentDidUpdate method is the place.
Note that the fact that this method has been invoked does not necessarily mean react has re-rendered the DOM. It only assures that the component is in sync with the DOM.
Do note that since setting state CAN cause a re-render, so you should really be careful about setting state in componentDidUpdate - or you might cause an infinite loop of sorts.
Even if you correctly implement shouldComponentUpdate, stopping the render cycle from ever reaching cDU- react docs suggest the result of sCU can be taken as a hint in the future, which will break your app in future releases.
So, if you must setState in cDU - you must check that this setState is required, and is not a side-effect of setting the state in the first place.

Resources