React Компонент перерендерывается, когда он мемоизирован через React.memo(), и пропс onClick сделан через useCallback()

import React from 'react';
import { useState, memo, useCallback } from 'react';
import reactLogo from './assets/react.svg';
import viteLogo from '/vite.svg';
import './App.css';
// import Button from './components/Button';

const Button2 = ({ children, onClick, ...args }) => {
   console.log('Button render');
   return (
      <button onClick={onClick} {...args}>
         {children}
      </button>
   );
};

function App() {
   const [count, setCount] = useState(0);
   const [input, setInput] = useState('');
   const MemoButton = React.memo(Button2);
   const handleInput = useCallback((e) => {
      setInput(e.target.value);
   });
   const handleClickButton = useCallback(() => {
      console.log('callback start');
      setCount((count) => count + 1);
   }, [setCount, count]);
   return (
      <>
         <div>
            <a href="https://vitejs.dev" target="_blank">
               <img src={viteLogo} className="logo" alt="Vite logo" />
            </a>
            <a href="https://react.dev" target="_blank">
               <img src={reactLogo} className="logo react" alt="React logo" />
            </a>
         </div>
         <h1>Vite + React</h1>

         <div className="card">
            <input type="text" value={input} onChange={handleInput} />
            <MemoButton onClick={handleClickButton}>+</MemoButton>
            <button>count is {count}</button>
         </div>
      </>
   );
}

export default App;

сделал мемоизацию компонента и useCallback для пропса

а если сделать мемоизацию компонента сразу

const Button = React.memo(({ children, onClick, ...args }) => {
   console.log('Button render');
   return (
      <button onClick={onClick} {...args}>
         {children}
      </button>
   );
})

то все работает, но мемоизировать компонент навсегда, думаю по оптимизации не хорошо, можно ли сделать мемоизацию для импортированного компонента?


Ответы (1 шт):

Автор решения: SwaD

Для того, что бы мемоизировать компонент в компоненте, необходимо использовать хук useMemo:

const buttonMemo = useMemo(() => <Button2 onClick={() => setCount(c => c + 10)}>
    {buttonState}
  </Button2>, 
[buttonState]);

Хук будет проверять массив зависимостей. Если значение в массиве зависимостей изменится, то будет пересчитано значение функции колбэка, иначе, возвращено сохраненное предыдущее значение(В данном примере, это JSX компонент)

Пример полного компонента

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

const Button2 = ({ children, onClick, ...args }) => {
   console.log('Button render');
   return (
      <button onClick={onClick} {...args}>
         Изменено {children} раз
      </button>
   );
};

const App = () => {
  const [count, setCount] = useState(0);
  const [buttonState, setButtonState] = useState(0);
  console.log('render app');
  const buttonMemo = useMemo(() => <Button2 onClick={() => setCount(c => c + 10)}>
    {buttonState}
  </Button2>, [buttonState]);
  
  return (
    <>
    <button onClick={() => setCount(count + 1)}>
      Нажато без изменений мемокнопки {count}
    </button>    
    {buttonMemo}
      <button onClick={() => setButtonState(c => ++c)}>Нажать для изменения мемокнопки</button>
    </>
  )
}

export default App

Этот же пример в песочнице

→ Ссылка