Skip to content

Experimental: Use use-context-selector lib instead of zustand lib for cross state management

Administrator requested to merge vk/experiment-use-context-selector into main

Created by: vovakulikov

DO NOT MERGE THIS

Context

Thanks to @fkling we have started thinking about global state and performance when we share this global state across components within the app. More concrete on that - Felix has introduced an experimental approach for state sharing with Zustand.

Felix PRs about that

Zustand's approach at the moment is in experimental mode. We still are looking for other/different/better/worst (heh) solutions. This PR also is experimental and tries to solve the same problem but in a slightly different way with use-context-selector lib.

Use context selector is a kind of low-level API for us compared with Zustand which allows to us achieve the same in performance but does not introduce the horizontal connection between components.

The problem with Zustand (as I see that)

Warning this is opinionated thoughts (probably I'm wrong or not actually - let's discuss it)

As I mentioned above Zustand goes with horizontal connections - what does that mean. Let's imagine we have a simple react app similar to this tree

Well, it isn't so big for this example but we will see the point. Let's also imagine that some components have a relation (if something has been changed in one component other components should react on that somehow)

With Zustand we will create a few small stores one store for one component relation. We will have something like this

With Zustand this works perfectly from a performance point of view and we have small and simple stores but when we increase the number of stores this is going to be really complicated from a mental modal perspective to understand all these connections between components and draw a clear schema in your mind.

With use-context-selector we take best from two worlds

As you can see all states passed with the top-down approach so it's easy to resolve global state consumers. All API of global state in one place and it is good because it also simplifies the mental modal of that solution. What about perf? Well, use-context-selector solves that with selectors way.

From use-context-selector doc

It will only accept context created by createContext. It will trigger re-render if only the selected value is referentially changed. The selector should return referentially equal results for the same input for better performance.

// CountComponent
const count1 = useContextSelector(context, v => v.count);
const setState = useContextSelector(context, v => v.setCount);

So <CountComponent/> won't be rerendered if something outside of its global state usage was updated. (This is the problem that usually React Context has and it's because React context in this case will ruin performance)

Conclusion

This PR replaces Zustand with the use-context-selector approach. At a first glance, this solution doesn't have any perf problem and solves prop drilling and perf exactly as Zustand does. Please feel free to play with that library and share your thoughts/feedback about that.

Merge request reports

Loading