1.0.0-alpha.19 • Published 2 years ago

react-hook-useobserver v1.0.0-alpha.19

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

React Hook useObserver.

Avoid excessive re-rendering in your React app by using useObserver and useObserverListener as an alternative to useState.

Both useState is frequently used hook. However, when you use the setState function, useState will immediately re-render the component. Most of the time, we don't want to re-render the component right away, and we want to make sure that we're re-rendering the component that listens to the change of a specific attribute in the state only. As an alternative, we can utilize useObserver to fix this problem.

The following are examples of common uses of useState.
import React, { useState } from 'react';
import {DataGrid} from './DataGrid'
function Example() {
    // Declare a new state variable, which we'll call "count"
    const [count, setCount] = useState(0);

    return (
        <div>
            <DisplayCount count={count} />
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
            <DataGrid/>
        </div>
    );
}

function DisplayCount({count}){
    return <p>You clicked {count} times</p>
}

The preceding scenario is an illustration of a common re-rendering issue in React application. We have an Example component with a state count, and Example component render the DataGrid component. In general, the DataGrid component is huge and contains numerous rows. If we don't conduct performance optimization, such as using React.memo, we will run into performance issue because React will re-render the entire component when the function from setCount is called.

An alternative to the above method using the useObserver is as follows:
import React, { useState } from 'react';
import {VeryComplexChart} from './VeryComplexChart'
function Example() {
    // Declare a new state variable, which we'll call "count"
    const [count, setCount] = useObserver(0);
    return (
        <div>
            <DisplayCount count={count} />
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
            <VeryComplexChart/>
        </div>
    );
}

function DisplayCount({count}){
    return <p>You clicked {useObserverValue(count)} times</p>
}

By changing useObserver instead, we have simply ensured that the DataGrid will not be re-rendered when setCount is invoked. When the setCount component is called, just the DisplayCount component is re-rendered.

What does calling useObserver do?

const [count,setCount] = useObserver(0);

It declares a observable variable. Our variable is called count, but we could call it anything else, like banana. This is a way to “preserve” some values between the function calls — useObserver is a new way to use the exact same capabilities that useState provides in react hook.

The primary distinction between useObserver and useState is the useObserver function's return an array of mutable values as well as the function used to setValue. The mutable value returned by useObserver is identical to the one returned by useRef.

As a result, we can utilize a property called "current" to access the current value of count.

return <div>{count.current}</div>

What are the benefits of using the useObserver?

useObserver will not re-render the component to which it is attached. So, if the component that utilizes useObserver hooks has extensive and complicated children, re-rendering, and performance degradation can be avoided.

How to listen if an observer is updated

To find out if an observer is updated when setValue is called, add an event listener to the observer itself. So basically we can rewrite our example above to something like this.

function Example() {
    const [count, setCount] = useObserver(0);
    return (
        <div>
            <DisplayCount count={count} />
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
            <VeryComplexChart/>
        </div>
    );
}

function DisplayCount({count}){
    const [countValue,setCountValue] = useState(count.current);
    useEffect(() => {
        const removeListener = count.addListener((newCount,oldCount) => {
            setCountValue(newCount);
        });
        return () => removeListener();
    },[]);
    return <p>You clicked {countValue} times</p>
}

The above code is an extended version of what we would do if we wanted to listen to the value of useObserver. We can use useObserverListener hook instead using useEffect to add event listener to the observer.

import {useObserverListener} from "./useObserverListener";

function DisplayCount({count}) {
    const [countValue,setCountValue] = useState(count.current);
    useObserverListener(count,(newCount) => {
        setCountValue(newCount);
    })
    return <p>You clicked {countValue} times</p>
}

We can cut the code above in half by utilizing the useObserverValue hook to receive the value from the observer.

import {useObserverValue} from "./useObserverValue";

function DisplayCount({count}) {
    const countValue = useObserverValue(count);
    return <p>You clicked {countValue} times</p>
}

By using useObserver you can avoid excessive re-rendering of react.