Firstly, I use the open source library Pullstate[1], which I find to be as effective as any alternative but _far, far_ simpler to understand and use.
All my components are functional components. For any state that _only_ that component needs, of course I simply use the useState hook.
When things need to be shared among multiple components, I create a Pullstate store - for instance, for an app I'm working on now I have a UiStateStore like so:
type UiStateStore = {
isSidebarOpen: boolean;
// etc
};
export const uiStateStore = new Store<UiStateStore>({
isSidebarOpen: false,
});
Then, in the components that need to know if the sidebar is open, I import this store and use its state hook _the exact same as the normal useState hook_, like so: const isSidebarOpen = uiStateStore.useState(s => s.isSidebarOpen);
You simply pick which properties you need off the state object ("s"). When you open the sidebar, you simply update the store like so: onClick={() => { uiStateStore.update(s => { s.isSidebarOpen = true; }) }}
And automatically, any components using uiStateStore.useState and watching the isSidebarOpen property will get updated, exactly the same as the normal useState hook - just shared.It's so dead simple and has made complex app-building so much easier for me.
By comparison, Redux in my experience has a harder learning curve with a lot of unnecessary pieces and boilerplate - it seems crazy to me that people think it's a great solution and it feels like people have convinced themselves all those pieces are "necessary", where in my experience they are anything but.
The one caveat is that if I have a component with many handlers, e.g. onClick, onMouseMove, onContextMenu, onMouseLeave, etc (and in some cases I do), components can get bloated. I haven't found a fix to that yet. But that's more an inherent issue with react than anything to do with state management.