Weekly - issue 38

Reloading a Document (and Preserving Query String Parameters) Using Only HTML

<a href="">Reload</a>

React Hooks and Javascript Closures

Everything in useEffect, useCallback hooks is a closure.

const HeavyComponentMemo = React.memo(HeavyComponent);

const Form = () => {
  const [value, setValue] = useState();

  const onClick = () => {
    // submit our form data here
    console.log(value);
  };

  return (
    <>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <HeavyComponentMemo
        title="Welcome to the form"
        onClick={onClick}
      />
    </>
  );
};

onClick changed between re-renders. We want the HeavyComponentMemo component is stable between re-renders.

const HeavyComponentMemo = React.memo(
  HeavyComponent,
  (before, after) => {
    return before.title === after.title;
  },
);

const Form = () => {
  const [value, setValue] = useState();

  const onClick = () => {
    // submit our form data here
    console.log(value);
  };

  return (
    <>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <HeavyComponentMemo
        title="Welcome to the form"
        onClick={onClick}
      />
    </>
  );
};

The value that we log in onClick is undefined, value‘s initial value. onClick changed between re-renders, but HeavyComponentMemo not, it still use the initial onClick.

We can append before.onClick === after.onClick in the React.memo comparison function, but we go back to the first version of the code.

We can wrap our onClick in useCallback, but useCallback depends on value, the value will change with every keystroke, the result still likes the first version of the code.

The key is to make onClick stable, and still can access the latest value. The following code do this:

const Form = () => {
  const [value, setValue] = useState();
  const ref = useRef();

  useEffect(() => {
    ref.current = () => {
      // will be latest
      console.log(value);
    };
  });

  const onClick = useCallback(() => {
    // will be latest
    ref.current?.();
  }, []);

  return (
    <>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <HeavyComponentMemo
        title="Welcome closures"
        onClick={onClick}
      />
    </>
  );
};

In ref.current?.(), ?. is the optional chaining operator, if ref.current is undefined or null, it will return undefined, not throwing an error.

The Mad Magazine Fold-In Effect in CSS

Amazing fold-in effect.