<div id="app"></div>
body {
  color: #def;
  font-family: sans-serif;
  background-color: #1d1e22;
}

main {
  padding: 4rem 2rem;
  display: flex;
  align-items: center;
  gap: 2rem;
}

form {
  flex: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
}

.card {
  flex: 1;
  border: 2px solid #def;
  border-radius: 6px;
  padding: 2rem;
}

.blink {
  animation: blink 0.35s;
}

@keyframes blink {
  0% {
    background-color: #f0f0;
  }
  25% {
    background-color: #f0f8;
  }
  100% {
    background-color: #f0f0;
  }
}
const blink = (el) => {
  el.classList.add('blink');
  setTimeout(() => {
    el.classList.remove('blink');
  }, 350);
};

const useBlink = (blinkOnProp, blinkOnRender, ref, prop) => {
  React.useEffect(() => {
    if (blinkOnProp) {
      blink(ref.current);
    }
  }, [prop]);
  
  React.useEffect(() => {
    if (blinkOnRender) {
      blink(ref.current);
    }
  });
};

const Child = ({ handler, blinkOnProp, blinkOnRender, description }) => {
  const ref = React.useRef(null);
  useBlink(blinkOnProp, blinkOnRender, ref, handler);
  return (
    <div ref={ref} className="card" onClick={handler}>
      {description}
    </div>
  );
};

const Parent = () => {
  const [_, setSymbol] = React.useState(Symbol());
  const [blink, setBlink] = React.useState(true);
  const test = () => {
    setSymbol(Symbol());
  };
  
  const handleSelectChange = (e) => {
    setBlink(e.target.value === 'onProp');
  }
  
  const handleClick = () => {};
  
  const memoizedHandleClick = React.useCallback(() => {}, []);

  return (
    <>
      <main>
        <Child
          description="Child component receiving memoized handler"
          handler={memoizedHandleClick}
          blinkOnProp={blink}
          blinkOnRender={!blink}
        />
        <Child
          description="Child component receiving handler saved as a variable"
          handler={handleClick}
          blinkOnProp={blink}
          blinkOnRender={!blink}
        />
      </main>
      <footer>
        <form>
          <select onChange={handleSelectChange}>
            <option value="onProp">Blink on prop change</option>
            <option value="onRender">Blink on render</option>
          </select>
          <button onClick={test} type="button">
            Test
          </button>
        </form>
      </footer>
    </>
  );
};

ReactDOM.render(
  <Parent />,
  document.getElementById('app'),
);
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/18.0.0/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.0.0/umd/react-dom.production.min.js