Skip to content
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

[Feat] Replace useMemo with useState for one time initializations #330

Open
andrewdoro opened this issue Apr 30, 2024 · 2 comments
Open
Labels
idea Neat Idea

Comments

@andrewdoro
Copy link

Target Use Case

We shouldn't have useMemo for one time initializations. While the current behaviour is the same as a useState, useMemo should only be used as an optimization. Future React releases might break this functionality.

https://tkdodo.eu/blog/use-state-for-one-time-initializations
React Forget

You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.

Proposal

Replace useMemo with useState for one time initializations. There might be more places where we need to change this:

const deck = useMemo(() => new GoogleMapsOverlay({interleaved: true}), []);

const controlContainer = useMemo(() => document.createElement('div'), []);

const glyphContainer = useMemo(() => document.createElement('div'), []);

@usefulthink
Copy link
Collaborator

TIL! Thanks for pointing this out! Would you be able to create a PR for this?

@usefulthink
Copy link
Collaborator

Thinking about this a bit more, what do we do about more complex cases or dependent intialization, for example

const geocodingLib = useMapsLibrary('geocoding');
const geocoder = useMemo(() => geocodingLib && new geocodingLib.Geocoder(), [geocodingLib]);

If the rule-of-thumb is "only use useMemo if the code would also work without it" - well, technically it does work, but not memoizing in these cases would trigger a dependency chain that could have a dramatic impact.

And this is true for all uses of useMemo I have reviewed so far (even including those you pointed out). The code will still work, but if react forgets about the memo it could incur some significant cost.

So how should we decide which pattern to use?

There are at least these options to choose from:

  • useMemo – should only be used if it would be ok for react to occasionally forget about it, but it also looks best and I personally think the name fits best and it does return the value and not an array to be destructured.

    const thing = useMemo(() => createThing());
    
    // with dependency
    const thing = useMemo(() => dependency && dependency.createThing(), [dependency]);
  • useState with lazy init / with useEffect for dependencies – this is also fine I guess, and everyone should be able to understand.

    const [thing] = useState(() => createThing());
    
    // with dependency
    const [thing, setThing] = useState();
    useEffect(() => { dependency && setThing(dependency.createThing()); }, [dependency]);
  • for the second case, we could even go one step further and introduce our own hook that would behave exactly like most people think useMemo does.

So what do we do with this? Any opinions?

@usefulthink usefulthink added enhancement New feature or request idea Neat Idea and removed enhancement New feature or request labels Aug 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
idea Neat Idea
Projects
None yet
Development

No branches or pull requests

2 participants