Skip to content
Advertisement

How to avoiding repeating work (or to keep common/shared state) in nested hooks?

In a nested hook: how could one know if it already was invoked in the current component (instance) and access any previously computed/saved values?

Preferably without the Component author/the hook user having to know about this and not having to do anything special for it to work.

Example

An example to illustrate the problem:

const useNestedHook = () => {
    // Some heavy work with the same result for each component instance.
    // Is it possible to remember the result of this work when
    // this hook is used again in the same component instance?

    // So I would like to save a state which all uses of useNestedHook
    // could access as long as they are in the same component instance.
}

const useHookA = () => {
    useNestedHook();
};

const useHookB = () => {
    useNestedHook();
};

const Component = () => {
    useHookA();

    // Would like to avoid useNestedHook repeating its work since it's
    // used in the same component and would have this same result (per definition)
    // Preferably without the Component author having to know anything about this.
    useHookB();
};

Imagined solution

Something like a “named” shared state, which would give access to the same shared state (in the same component instance) no matter which hook it’s used in. With each component instance having its own separate state as usual. Maybe something like:

const [state, setState] = useSharedState("stateId", initialValue);

Answer

No, that is not possible. Each useState() call will always be separate from other useState() calls.

The component can not use the hooks as in your example, but the component author doesn’t necessarily have to care for the implementation details.

A solution would depend on the use case.

Some details:

One state is defined by where the useState() call is written in the code (see explanation), which is not directly related to the instance. I.e. two useState() calls and two instances are 4 state values.

You can use shared state e.g. using context, but then the state would also be shared by all instances, not only the hooks (which you don’t want).

So the useNestedHook() will always be “separate”, but if you can use shared state, and you only care for “caching”, and can accept that the useNestedHook() is called twice (i.e. skip the expensive operation if the result is the same), then you can use useEffect(). I.e. the call would depend on the value, not the instance and not the hook.

Some examples:

1. One hook with options

E.g. if your hooks A and B would optionally calculate two different values, which need the same useNestedHook() value, you could create one hook with options instead, e.g.:

const useHookAB = ({ A, B }) => {
  const expensiveValue = useNestedHook();
  if( A ){ /* do what useHookA() was doing */ }
  if( B ){ /* do what useHookB() was doing */ }
};

const Component = () => {
    useHookAB({ A: true, B: true });
};

I can’t imagine another reason right now why you would want to call the hooks like that.

2. The “normal” way

The obvious solution would be:

const useHookA = ( value ) => {
  // ...
};

const useHookB = ( value ) => {
  // ...
};

const Component = () => {
    const value = useNestedHook();
    useHookA( value );
    useHookB( value );
};

But I can imagine reasons why you can’t (or don’t like to) do it that way.

Advertisement