Readundant states – DEV Community
December 25, 2024

Readundant states – DEV Community

The most common problem I encounter when working on multiple React projects is unnecessary state.

This “bad” state primarily has a negative impact on code quality and its maintenance and development. They can also adversely affect an application’s performance or cause surprising errors that are difficult to diagnose.

In this article I will try to show an example of a redundant state and discuss the potential problems it causes and how to rebuild it quickly and safely.


question

This example will be based on a real case. I’ll try to keep the code structure relatively primitive but simplified. Of course, in many places this requires more refactoring. I do this intentionally because in real projects it’s not always possible to spend days or weeks refactoring the entire code. Sometimes we have to settle for a quick win.

function useUserLoader() {
  const { data: users } = useQuery({
    queryKey: ["userList"],
    queryFn: () => getUserList(),
    placeholderData: [],
  });

  return { users };
}

function useUsersOptionLoader() {
  const [userOptions, setUserOptions] = useState([]);
  const { users } = useUserLoader();


  useEffect(() => {
    if (users) {
      setUserOptions(users.map(mapUserToOption));
    }
  }, [users]);

  return userOptions;
}
Enter full screen mode

Exit full screen mode

In this case, initially we would have 4 Present. If you don’t believe me, as proof I’ll add a screenshot of the analyzer.

Let’s take a step-by-step look at what happens in each render.

first render

  1. In this step, useUserLoader Starts fetching data from the server and returns an empty list, which is placeholderData pillar.
  2. useUsersOptionLoader initialization userOptions Status is an empty list.
  3. useEffect Calls a hook that updates the userOptions state with a new value (an empty array).
  4. useUsersOptionLoader Return to current status userOptionswhich is an empty array

Second rendering:

  1. This rendering occurs because userOptions The status has changed.
  2. In this step, useUserLoader Still getting data from the server and returning an empty list.
  3. this useEffect The hook is not called because users The value has not changed.
  4. useUsersOptionLoader Return to current status userOptionswhich is still an empty array.

third rendering

  1. In this step, useUserLoader Completes retrieving data from the server and returning the filled user list.
  2. this userOptions state is still an empty list.
  3. useEffect The hook is called and the userOptions state is updated with the new value, which is the user’s mapped array.
  4. useUsersOptionLoader Return to current status userOptionswhich is still an empty array.

fourth render

  1. In this step, useUserLoader Returns the list of users obtained in the previous render.
  2. The userOptions state is a mapped array of users, set in a previous render.
  3. this useEffect The hook is not called because the user value has not changed.
  4. useUsersOptionLoader Returns the current state of userOptions, which is a mapped array of users.


Key points:

  • Three renders return an empty array (the first, second, and third), and the userOptions state is updated only in the third render after the useEffect hook is called.
  • The fourth render reflects the updated userOptions state, which is now populated with the mapped users array.

In large applications that render large amounts of data, these 4 renderings can become a problem. However, in most cases, this isn’t a big deal.

The next problem with this code is that you need to spend extra time to understand what is happening inside useUsersOptionLoader. The combination of useEffect and useState introduces additional complexity. While this is a relatively simple hook, in more complex cases you may encounter hooks with multiple lines of code, multiple states, and numerous dependencies. In this case you can easily spend hours analyzing and trying to understand the logic, and after making changes you may end up breaking other parts of the code.


solution

The solution to this kind of code is simple: remove useEffect and useState and simply return the mapped value.

function useUsersOptionLoader() {
  const { users } = useUserLoader();
  return users.map(mapUserToOption);
}
Enter full screen mode

Exit full screen mode

In this case, initially we would have 2 Present. If you don’t believe me, as proof I’ll add a screenshot of the analyzer.

Let’s take a step-by-step look at what happens in each render:

First render:

  1. useUserLoader Starts retrieving data from the server and returns an empty list, as defined in the placeholderData attribute.
  2. useUsersOptionLoader Returns an empty array.

Second rendering:

  1. useUserLoader Completes retrieving data from the server and returning the filled user list.
  2. useUsersOptionLoader Returns an array of mapped users.

There are now only two renders, instead of the previous four, that are directly related to the material being retrieved useUserLoader. In addition, the program code is more concise and has no redundant content. useEffect or useState hook. this useUsersOptionLoader The code is now clearer and more direct, making it easier to understand what’s going on.


Summarize

By making simple improvements to your code, you can optimize your application’s performance, reduce your code, and make it more readable. In the next article, I will show another example of redundant state and a simple solution to improve the application.

2024-12-25 08:57:26

Leave a Reply

Your email address will not be published. Required fields are marked *