React hooks

Starter pack

Let's see with React Three Fiber what React hooks we will commonly use and how to use them while avoiding common pitfalls.

If you are not familiar with React hooks, I recommend you to read the official documentation first.

useState

useState allows you to create a state variable and a function to update it.

const [color, setColor] = useState("white");

It returns an array with two elements:

  • the state variable
  • the function to update it.

You can use it like this:

import { useState } from "react";

// ...

const Cube = (props) => {
  const [color, setColor] = useState("white");

  useControls({
    changeColorToRed: button(() => setColor("red")),
    changeColorToBlue: button(() => setColor("blue")),
    changeColorToGreen: button(() => setColor("green")),
  });
  return (
    <mesh {...props}>
      <boxGeometry />
      <meshStandardMaterial color={color} />
    </mesh>
  );
};

// ...

Every time you click on one of the buttons, the color of the cube changes.

Because updating the state triggers a re-render, you should avoid calling useState inside a loop or a condition.

We will see the good way to do frequent updates with r3f in the React Three Fiber hooks lesson, and discuss the common pitfalls to avoid in the optimization lesson.

For the moment, here are some rules of thumb with useState:

  • Avoid calling useState for fast-changing values.
  • Use it at the deepest level possible. That way, you will avoid re-rendering items that don't need to be re-rendered.

useMemo

useMemo allows you to memoize a value. This means that the value will be computed only when its dependencies have changed.

Let's replace our material with a memoized one:

import { useMemo, useState } from "react";

// ...

const Cube = (props) => {
  // ...
  const material = useMemo(() => <meshStandardMaterial color={color} />, []);

  return (
    <mesh {...props}>
      <boxGeometry />
      {material}
    </mesh>
  );
};

// ...
  • The first argument is a function that returns the value to memoize. In our case, it is a material component.
  • The second argument is an array of dependencies. We passed an empty array, which means that the material will be computed only once.
  • The returned value is the memoized value.

Now, when we click on the buttons, the cube color doesn't change, because the material is not re-computed.

To fix this, we need to pass the color as a dependency:

const material = useMemo(() => <meshStandardMaterial color={color} />, [color]);

Now, when we click on the buttons, the cube color changes, because the material is re-computed when the color state changes.

We did this to show you how useMemo works, but in this case, it is not necessary to memoize the material. React Three Fiber already do the necessary optimizations for us.

Cases where useMemo can be benficial are:

  • When a value is expensive to compute on a component that renders frequently.
  • When a component has a lot of children and you want to avoid re-rendering them.
  • When a component has a lot of props and you want to avoid re-rendering it.

Don't use useMemo for premature optimization when you begin with React and r3f. It can be tricky to use and can lead to bugs. Prefer using it when you have performance issues.

useRef

useRef allows you to create a mutable value that persists across renders.

It is commonly used to store a reference to a DOM element or a Three.js object but you can use it to store any value.

The good thing about useRef is that it does not trigger a re-render when its value changes. With r3f, this is perfect for animations or any value that changes frequently.

End of lesson preview

To get access to the entire lesson, you need to purchase the course.