Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discuss useRef usage #1009

Closed
thaisguigon opened this issue Jul 31, 2023 · 5 comments
Closed

Discuss useRef usage #1009

thaisguigon opened this issue Jul 31, 2023 · 5 comments
Assignees
Labels
blocked: tech spec needed scope: front Issues that are affecting the frontend side only

Comments

@thaisguigon
Copy link
Contributor

thaisguigon commented Jul 31, 2023

https://react.dev/reference/react/useRef

useRef is a React Hook that lets you reference a value that’s not needed for rendering.

As discussed with @magrinj and @emilienchvt, we would like to reassess how we can use this hook for better performance. Right now it is mostly only used for manipulating the DOM with HTML element refs but it could be useful in other cases where we want to keep reference to a value without triggering re-renders.

@thaisguigon thaisguigon self-assigned this Jul 31, 2023
@thaisguigon thaisguigon moved this from 🆕 New to 📋 Backlog in Product development ✅ Jul 31, 2023
@thaisguigon thaisguigon added blocked: tech spec needed scope: front Issues that are affecting the frontend side only labels Jul 31, 2023
@magrinj
Copy link
Member

magrinj commented Jul 31, 2023

I completely agree. While useRef is often used for referencing mutable DOM elements, its utility goes far beyond that. As pointed out in the React documentation, it can be used to maintain a reference to a value that does not affect the render output. This can include values from a setInterval operation, timers, or other data sources that need to persist across multiple render cycles.

The major advantage here is that changes to these references do not trigger component re-renders, which can significantly improve performance in scenarios where the reference is updated frequently but the view does not need to be refreshed.

Beyond this, I think useRef could be beneficial in scenarios involving complex calculations or tasks that we wouldn't want to repeat unnecessarily due to re-renders. For example, we might store the results of such computations in a ref, ensuring that we only perform them once and use the stored result thereafter.

In essence, we can look at useRef as a mechanism for storing "memory" or "state" that doesn't affect the render output but might be important for the logic of our components. This can open the door to more performant and efficient components. Looking forward to exploring this further with the team!

@magrinj
Copy link
Member

magrinj commented Jul 31, 2023

cc @charlesBochet @lucasbordeau

@charlesBochet
Copy link
Member

charlesBochet commented Aug 1, 2023

pHappy to discuss it,
My opinion is that we should avoid it except when using it to manipulate DOM reference and rare cases:
To me:

  • useRef is very similar to useState. If we do need to do complex computations useCallback (+ useState) can achieve the same output.
  • they are rare cases where we need it (I think setInterval is one if you want to be able to clearInterval)
  • if we are using it to prevent a variable change to trigger a re-render of a complex component, we can extract this variable to another component that would live beside the complex component. (see GenericEntityTableData for instance)

As this is introducing a second way to do things we can do with setState with the same complexity (TBH, I personally think that useRef API actually complexifies code reading but this is subjective), I would strongly advocate to have one way of doing things. This will help keeping the codebase uniform, and prevent discussions on either we should use useRef or useState in "THIS" specific cases (which will likely never ends are they are similar). Keeping clear coding conventions is an important winning condition on this project.

In a nutshell, I would really push to keep only using useState (+ useCallback) on the project, except if there is no easy way to do so. Do you have a particular example into mind where you would really need useRef or it it significantly more convenient?

@lucasbordeau has refactored the whole table component (which is by far the most complex we have), there are no more re-render on table, it's performant and no useRef have been used

@magrinj
Copy link
Member

magrinj commented Aug 1, 2023

Appreciate your perspective on this @charlesBochet. While I understand your concern for keeping the codebase clean and streamlined, I believe that useRef could provide us with certain advantages in specific contexts.

useRef does indeed share similarities with useState and useCallback. However, there are a few key distinctions that make useRef a good way to go in some case:

  1. Non-triggering of re-renders: useRef does not cause a re-render when the referenced value changes. This can be a significant performance optimization in scenarios where a value changes frequently but doesn't impact the rendered output.

  2. Persistence of data: The useRef hook provides us with a way to persist data across the entire lifecycle of a component. Unlike local variables which get wiped out after every render, a ref persists without causing additional renders.

  3. Broad applicability: Beyond DOM elements, useRef is ideal for holding any mutable value, especially those that do not influence the component’s output. This could include interval IDs, timers, Promises, external mutable sources, computed values from heavy operations, and more.

A practical example of using useRef could be when we perform an expensive computation or an API call on the first render and want to keep this value for the entire lifecycle of the component. If the component re-renders due to unrelated prop or state changes, the value in useRef would still persist, thus avoiding unnecessary recalculations or repeated API calls. This can lead to significant performance improvements, especially in components that re-render frequently.

As for the refactor by @lucasbordeau, it's a really great job! However, the shared state management in that case involves different considerations and might not directly apply to all situations where we could potentially benefit from useRef.
In this refactor we're using a lot of recoilState, that are also using useRef internally in the same way we suggested above:
https://github.com/search?q=repo%3Afacebookexperimental%2FRecoil%20useRef&type=code

In conclusion, for me, while useState and useCallback should be our go-to in most cases, let's also consider useRef as a performance optimization tool in specific scenarios. I believe using the right tool for the right job can result in a more robust and efficient codebase.

@emilienchvt
Copy link
Contributor

emilienchvt commented Aug 2, 2023

From the react documentation, community threads and various articles, useRef is a valid implementation of a data container that can be used throughout the component's lifecycle, improving performance by preventing re-renders (unlike useState).
That being said, it introduces mutability, coming with some pitfalls (also detailed in the doc). There is also a risk that using it as a state or derived state brings us to force re-renders, defeating the purpose of key react features.

I tend to agree we should restrain its usage to:

On storing the result of complex calculations, I tend to agree with @charlesBochet 's point to have it in a separate component as it prevents re-renders and brings code separation without the inconvenience of useRef.

Also, on the point that recoil is using refs, IMO it is great that we can leverage the perf optimisation of refs with a nicer API (note that recoil's selectors are also a way to handle complex calculations for derived state).

@twentyhq twentyhq locked and limited conversation to collaborators Aug 9, 2023
@charlesBochet charlesBochet converted this issue into discussion #1143 Aug 9, 2023
@github-project-automation github-project-automation bot moved this from 📋 Backlog to ✅ Done in Product development ✅ Aug 9, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
blocked: tech spec needed scope: front Issues that are affecting the frontend side only
Projects
Archived in project
Development

No branches or pull requests

4 participants