We Might Not Need an Effect #1

May 5, 2024

What this article series are about 🔍

This article series are basically a summary of You Might Not Need an Effect, which is a React official documentation about the usage of useEffect hook in React.

The original article is really really good, for sure better than my summary. So, if you have time, I highly recommend you to read the original article.

My motivation is just to provide a quick summary both for myself and for others who might be busy or a bit lazy to read through the whole article.

Yeah, it's for you 😉 haha

I summarized the content as short as possible, so we can get main idea quickly. So don't forget to check the original article for more detailed information. Again, it's really good learning material.

When something can be calculated from the existing props or state, we don't need extra state and useEffect

Bad 👎

function Form() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');

  // 🔴  Unnecessary extra state and useEffect
  const [fullName, setFullName] = useState('');
  useEffect(() => {
    setFullName(firstName + ' ' + lastName);
  }, [firstName, lastName]);
  //

  return (
    <div>
      <input
        value={firstName}
        onChange={(e) => setFirstName(e.target.value)}
        placeholder="First Name"
      />
      <input
        value={lastName}
        onChange={(e) => setLastName(e.target.value)}
        placeholder="Last Name"
      />
      <h1>{fullName}</h1>
    </div>
  );
}

Good 👍

function Form() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');

  // ✅  No extra state and useEffect
  const fullName = firstName + ' ' + lastName;

  return (
    <div>
      <input
        value={firstName}
        onChange={(e) => setFirstName(e.target.value)}
        placeholder="First Name"
      />
      <input
        value={lastName}
        onChange={(e) => setLastName(e.target.value)}
        placeholder="Last Name"
      />
      <h1>fullName</h1>
    </div>
  );
}

Takeaway

The point is to let re-render do the calculation, instead of doing it with extra state and useEffect. When we call setFirstName or setLastName, we trigger a re-render, and then the next fullName will be calculated from the fresh data.

Reference

When we use useEffect just to do re-calculation on some props or state change, what we really need is useMemo.

Bad 👎

getFilteredTodos is a function that filters todos based on the filter, and it is used to calculate visibleTodos. Maybe you want to update visibleTodos only when todos or filter changes.

function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');

  // 🔴  Unnecessary extra state and useEffect
  const [visibleTodos, setVisibleTodos] = useState([]);
  useEffect(() => {
    setVisibleTodos(getFilteredTodos(todos, filter));
  }, [todos, filter]);
 ...
}

Good 👍

function TodoList({ todos, filter }) {
  const [newTodo, setNewTodo] = useState('');

  // ✅  No extra state and useEffect, but efficient use of useMemo
  const visibleTodos = useMemo(
    () => getFilteredTodos(todos, filter),
    [todos, filter]
  );
}

Takeaway

If your motivation to use useEffect is just to update something only when some props or state is updated, what you really need is useMemo.

Reference

When we want to reset the state based on some prop change, we could do it witout useEffect

Bad 👎

function List({ items }) {
  const [isReverse, setIsReverse] = useState(false);
  const [selectedItem, setSelectedItem] = useState(null);

  // 🔴 Avoid: resetting state on prop change in an Effect
  useEffect(() => {
    setSelection(null);
  }, [items]);
  // ...
}

Good 👍

function List({ items }) {
  const [isReverse, setIsReverse] = useState(false);
//   Change selectedItem to selectedItemId, so that we don't need to reset state on prop change
  const [selectedItemId, setSelectedItemId] = useState(null);

  // ✅  No extra state and useEffect, just calculate selectedItem when selectedItemId changes
  const selectedItem = useMemo(items.find((item) => item.id === selectedItemId);,[items, selectedItemId]);
}

Takeaway

When you adjusting or resetting the state based on some prop change, you better to think about the way to do it without useEffect

Reference

See you again in #2 !

This is it! 🎉 Thank you for reading!