An Important Lesson About React Hooks

Written by xthecapx | Published 2022/08/23
Tech Story Tags: react | reactjs | react-hook | react-hooks | reactjs-development | guide | beginners-guide | how-to

TLDRHackernoon has been nominated for the #[noonies2022/internet-heroes/2022-contributor-of-the-year-learning award. To celebrate his nomination, I decided to write a short article explaining my findings. Today I learned something important about the react hooks, and to celebrate my nomination I wrote this article. The goal was to create a data layer to sync up the data of the server with the client state. The data layer is read-only for the consumers, and the data mutation is limited to the first element in the dom tree that uses hook.via the TL;DR App

Today I learned something important about the react hooks, and to celebrate my nomination to the #noonies2022, I decided to write a short article explaining my findings.

The react hooks are regular functions.

When you create a new useSomething hook, you make a new javascript function. It could sound like routine to some of you. However, you could forget the basics after using custom hooks from popular libraries like redux, react query, mobx, etc.

So, let me explain to you my thoughts today.

Today's goal was to create a data layer to sync up the data of the server with the client state. So, I naturally decided to create a custom hook to encapsulate the logic and facilitate using the data layer across the App.

My initial code looks like this:

export const useData = (initalStatus) => {
  const [data, setData] = useState(initalStatus);

  return {
    data,
    setData,
  };
};

The component structure I had in my mind was something like this:

Because StepOne and StepTwo are children of the App, and according to the diagram I created, I thought the data state would be the same for the three components due to the React magic. Isn't it?

The answer is no. Every time you call a hook inside the scope of a component, the useState creates a new instance of the object at the component level. So there is no connection between the state of the components other than using the same function to make it.

So, How Can I Create a Single Data Layer for the Whole Component's Family?

First, I tried to solve the problem using basic javascript knowledge. My mental process was: Create a global variable at the level of the custom hook. Then, use an useEffect to sync the external object with the internal state to force a re-render at the consumer level. Finally, I expose the data by returning it.

The custom hook with the manual cache implementation was something like this:

let dataCache;
const updateCache = (newData) => {
  dataCache = newData;
};
export const useData = (initalStatus) => {
  const [data, setData] = useState(initalStatus);

  useEffect(() => {
    if (!dataCache || dataCache !== data) {
      updateCache(data);
    }
  }, [data]);

  return {
    data,
    dataCache,
    setData,
    updateCache: (newData) => {
      updateCache(newData);
      setData(newData);
    },
  };
};

Can you tell me what the limitation of my solution is? Because I'm using an object outside the react library scope, I cannot start a re-rendering on the parent components. So, we must remember the internal state will trigger a re-render in the current element and his children, but not in the grandparent. So, the solution will work as long all the components that need access to the data layer are ancestors. Also, it's read-only for the consumers, and the data mutation is limited to the first element in the dom tree that uses the hook.

Is It Possible to Create a Custom Hook That Forces a Re-Render in Every Component When Required?

I started researching how to create something like a singleton custom hook in React. It didn't take much time until I found two libraries that got my attention. First, I found "React singleton hook" with 141 stars on Github, and a Reddit thread suggested I try "Use between" with 181 stars. Luckily, both solved my problem of re-rendering the parent and allowed me to use a custom hook like a React Context.

I don't want to dive into the implementation details of both libraries. However, from a general perspective, the React singleton hook is the code I was trying to generate with a global variable, which includes re-rendering the parent component from the children. In contrast, the use-between library uses an observer pattern to address the problem.

I think it's a good idea if somebody writes a blog post explaining the Javascript pattern behind those libraries. I love to read it!

If you want to play with the code, I created for this article; I created this instance of Stackblitz with my findings.

How Can I Create a Data Layer With the React Available Tools?

Finally, after all my research, I solved the issue using one of the well-documented React tools. I realized that what I was trying to create was the React Context.

Again, this post intends not to explain a React concept thoroughly but to show you my thinking process. Nevertheless, The React Context, in a nutshell, uses the createContext function, which creates the provider and the consumer reference. Then, the provider manages the re-rendering of the components, and the reference + useContext hook exposes the desired data to the consumer.

Once I implemented the React Context, everything started to work as expected, and just after I finished, I decided to share my knowledge with this blog post.

I hope you learned something new or that this lecture allowed you to refresh your memory about some basic javascript knowledge. Sometimes taking a moment to reevaluate your solution helps to deliver quality, well-design, robust code within a reasonable time.

Happy coding to everybody. Remember to vote for me in the #noonies2022, and as I always say Good science! Before you finish your reading, and if you're a Spanish speaker, please consider listening to my podcast #programadoresAnónimos 😀.


Written by xthecapx | Javascript Developer
Published by HackerNoon on 2022/08/23