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

usememo-overdose #37

Open
utterances-bot opened this issue Aug 26, 2023 · 11 comments
Open

usememo-overdose #37

utterances-bot opened this issue Aug 26, 2023 · 11 comments

Comments

@utterances-bot
Copy link

useMemo overdose | Edvins Antonovs

When overuse of useMemo hook becomes a problem

https://edvins.io/usememo-overdose

Copy link

I've created ESLint plugin to detect unnecessary useMemo https://github.com/shashkovdanil/eslint-plugin-usememo-recommendations

Copy link

what about if a memo is used to create a reference object and pass it as a prop? what about politics - avoid unnecessary rendering rather than doing a simple calculation?

Copy link

Absolutely wrong way to test performance of the memo:

  const startTime = performance.now();
  const result = expensiveCalculation;
  const endTime = performance.now();

Copy link
Owner

@shashkovdanil Nice work out there!

Copy link
Owner

@sergeysova Hey, can you please elaborate on this? Why it's wrong?

@ummahusla
Copy link
Owner

@Devisione

What about if a memo is used to create a reference object and pass it as a prop?

Yup, I like this idea. I assume we are speaking about something like this idea.

import React, { useState, useMemo } from 'react';

// ChildComponent receives a reference object as a prop
const ChildComponent = ({ data }) => {
  console.log('ChildComponent rendering');
  // Use the data from the prop
  return <div>Data: {data.value}</div>;
};

// ParentComponent creates a reference object and passes it to ChildComponent
const ParentComponent = () => {
  const [count, setCount] = useState(0);

  const referenceObject = useMemo(() => {
    return { value: count };
  }, [count]);

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <button onClick={handleClick}>Increment Count</button>
      <ChildComponent data={referenceObject} />
    </div>
  );
};

export default ParentComponent;

what about politics - avoid unnecessary rendering rather than doing a simple calculation?

I stay away from politics in general and bring systems instead. So, if you can agree within a team on what approach to use - stick to it.

Copy link

yarastqt commented Sep 4, 2023

@ummahusla I guess in this sample expensiveCalculation used as result of calculation but not as calling

Copy link

amiiit commented Sep 7, 2023

@ummahusla if I'm reading this correctly, this code:

  const startTime = performance.now();
  const result = expensiveCalculation;
  const endTime = performance.now();

Is measuring the time that JS takes to create a new reference in memory and assign it the value of another reference. It does not call any actual function that would do any work worth measuring. expensiveCalculation contains the result of the memo. So the result for endTime - startTime will be constant and almost zero every time.

Copy link

amiiit commented Sep 7, 2023

@ummahusla another use-case I found myself using useMemo for is in order to avoid unnecessary renders. For example if I need to convert one value to some other shape and for this I create a new object. The calculation is cheap and I don't mind creating a new object, but it will trigger a render in the lower parts of the component tree, which I want to avoid.

Would you still recommend useMemo for this, or do you have another pattern?

@ummahusla
Copy link
Owner

@ummahusla if I'm reading this correctly, this code:

  const startTime = performance.now();
  const result = expensiveCalculation;
  const endTime = performance.now();

Is measuring the time that JS takes to create a new reference in memory and assign it the value of another reference. It does not call any actual function that would do any work worth measuring. expensiveCalculation contains the result of the memo. So the result for endTime - startTime will be constant and almost zero every time.

Hey, thanks for your reply. I think I see where you coming form. I'm playing around with this component right now and will update the blog post at some point:

import React, { useState, useMemo } from "react";

const MemoComponent = () => {
  const [count, setCount] = useState(0);

  const expensiveCalculation = useMemo(() => {
    const startTime = performance.now();

    // Simulate an expensive calculation
    let result = 0;
    for (let i = 0; i < 10000000; i++) {
      result += i;
    }

    const endTime = performance.now();
    console.log("Expensive calculation execution time:", endTime - startTime);

    return result;
  }, []); // Include 'count' as a dependency if you want to recompute when it changes.

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <button onClick={handleClick}>Increment Count</button>
      <p>Count: {count}</p>
      <p>Result: {expensiveCalculation}</p>
    </div>
  );
};

export default MemoComponent;

@ummahusla
Copy link
Owner

@ummahusla another use-case I found myself using useMemo for is in order to avoid unnecessary renders. For example if I need to convert one value to some other shape and for this I create a new object. The calculation is cheap and I don't mind creating a new object, but it will trigger a render in the lower parts of the component tree, which I want to avoid.

Would you still recommend useMemo for this, or do you have another pattern?

@amiiit My blog post was published on the /r/reactjs subreddit, and it was a blast. Lots of healthy discussions and related articles were posted, have a look around.

One link in particular I found interesting - https://attardi.org/why-we-memo-all-the-things/, I even emailed its author, to figure out whenever anything changed. This is his response:

Nothing changed. If anything we doubled down. I helped create a team called Client Foundations responsible for front-end code at the entire company. I personally led multiple efforts to fix performance of various React and React Native codebases and the biggest gains we’ve seen came from adding memoization where it was missing. And we also wrote custom lint rules to enforce memoization everywhere.
I think the solution is for React to build a compiler to memoize things automatically so we can end this debate once and for all. I think it’s coming.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants