Now for our third topic, we'll discuss how to reduce the number of components in a tree that render. If our state is very high up, the entire app is going to re-render. Effect components does let us reduce how often this happens, but it does still need to happen at some point.
Now what if you have a large list or a piece of data is only used by maybe a time stamp in some list items? Do we really need to re-render the entire list just to update a few strings? We could use context to pass a value down from the grandparent to his grandchild, but re-renders still need to happen because we're using React.useState.
But what if we use state without useState? We would need to do what React does for us, store a piece of state, have a way to set that state, and also inform everyone who cares that the state changed. Well, React does do this pretty well, but other libraries can do it too. So let's just go ahead and start using a whole second competing UI library, Svelte.
The trick is, we're not actually using Svelte for his UI code. Instead, we'll unwrap the package and just use a special store API. Svelte stores act like a portable use state. They have a set method to update the state and components watching the store. You can even pass in a callback to update state based on the previous state value, just like React. The store API is independent of the rest of Svelte, so we don't need to worry about pulling in anything else. You can use eslint to disable direct Svelte imports in your code, and it only allows importing from Svelte slash store.
Now instead of passing around a raw state value, we can pass around a Svelte store instead. You can do so using props or context, like regular values. The store does not change identity, so changing values will not affect React.memo. Now, at some point, we do still need to convert from the Svelte store back into React.state. Instead of placing the hook inside the ancestor branch component, we can place multiple hooks inside each leaf component. We'll use React's built-in usync external store hook. It takes two callbacks, one used to subscribe to the store and watch for changes, and another to get the current value from the store. Whenever the store changes, React will use that as a signal to update the hook's internal state using the current value of the store, causing the component to re-render.
Now the intermediate components don't need to re-render at all. Only the leaf components get notified when the store changes, so no other components get re-rendered. By just passing around a static store object, we've broken out of the limitations of React's useState hook while keeping a similar API. Combined with effect components, we can prepare state and then pass it around without re-rendering the ancestors at all. By passing store.set to our effect component instead of React's setState callback, we never need to work with useState in our top component and therefore never need to re-render it. DataModel stores are also usable outside of React or any UI library, so you can manage reactive state in data model classes, in a worker, or even on the server. This is actually very similar to a performance optimization Redux does under the hook. Redux is just passing around a static store reference using React context.
Comments