Preact — Memoization, Callbacks, and Context Hooks

John Au-Yeung - Jan 22 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Preact is a front end web framework that’s similar to React.

It’s smaller and less complex than React.

In this article, we’ll look at how to get started with front end development with Preact.

Memoization

We can use the useMemo hook to store results of expensive computations.

For example, we can write:

import { render } from "preact";
import { useMemo, useState } from "preact/hooks";

const expensive = (a, b) => {
  //...
};

function App() {
  const [a, setA] = useState(0);
  const [b, setB] = useState(0);
  const memoized = useMemo(() => expensive(a, b), [a, b]);
  //...
  return (
    <div>
      <p>{memoized}</p>
    </div>
  );
}

if (typeof window !== "undefined") {
  render(<App />, document.getElementById("root"));
}
Enter fullscreen mode Exit fullscreen mode

We have the expensive function that does some expensive operation.

We call it in the useMemo callback to cache its result.

memoized is only computed again when a or b is changed.

We shouldn’t commit any side effects in useMemo .

If we need to commit side effects, we should use useEffect .

useCallback

The useCallback hook lets us ensure that the returned function is referentially equal until the dependencies change.

For example, we can write:

import { render } from "preact";
import { useCallback, useState } from "preact/hooks";
function App() {
  const [count, setCount] = useState(0);
  const increment = () => setCount(count + 1);
  const decrement = () => setCount((currentCount) => currentCount - 1);

  const log = useCallback(() => console.log(count), [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={log}>log</button>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
}
if (typeof window !== "undefined") {
  render(<App />, document.getElementById("root"));
}
Enter fullscreen mode Exit fullscreen mode

We pass in a callback into the useCallback hook to cache the callback until the count state changes.

This lets us prevent the callback from recreated every time the component renders.

useRef

The useRef hook lets us get a reference to a DOM node in a function component.

For instance, we can write:

import { render, Fragment } from "preact";
import { useRef } from "preact/hooks";

function App() {
  const input = useRef(null);
  const onClick = () => input.current && input.current.focus();

  return (
    <>
      <input ref={input} />
      <button onClick={onClick}>Focus input</button>
    </>
  );
}
if (typeof window !== "undefined") {
  render(<App />, document.getElementById("root"));
}
Enter fullscreen mode Exit fullscreen mode

We call the useRef hook to return a ref.

Then we assign it to the input with the ref prop.

We also create the onClick function to get the input element with the input.current property and call focus on it.

So when we click on Focus Input, the input will be focused.

useContext

The useContext hook lets us access a context in a function component.

For example, we can write:

import { createContext, render, Fragment } from "preact";
import { useContext } from "preact/hooks";

const Theme = createContext("light");

function DisplayTheme() {
  const theme = useContext(Theme);
  return <p>Active theme: {theme}</p>;
}

function SomeComponent({ children }) {
  return <>{children}</>;
}

export default function App() {
  return (
    <div>
      <Theme.Provider value="dark">
        <SomeComponent>
          <DisplayTheme />
        </SomeComponent>
      </Theme.Provider>
    </div>
  );
}

if (typeof window !== "undefined") {
  render(<App />, document.getElementById("root"));
}
Enter fullscreen mode Exit fullscreen mode

We call createContext to create the Theme context.

Then we render the Theme.Provider in app to set the value of the context.

And we can get the Theme context value with the useContext hook in the DisplayTheme component.

We pass in the context that we want to access.

If our component is inside the Theme.Provider then we can access its value.

Conclusion

We can use hooks to memorize data, cache callbacks, and share data between components with Preact.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .