spierala / mini-rx-store

MiniRx - The reactive state management platform
https://mini-rx.io
Other
170 stars 9 forks source link

`configureStore` was called multiple times. when usin SSR #222

Open matejvorcak opened 2 months ago

matejvorcak commented 2 months ago

I want to add Server-Side Rendering (SSR) functionality to my app, which currently uses @mini-rx as a store manager. You'd like to create a new instance of the store for each request, but I am encountering a "configureStore was called multiple times" error on all requests except the first one. The issue arises because the library is designed to use a singleton store. Is there a workaround to use @mini-rx with SSR?

spierala commented 2 months ago

Hey @matejvorcak

Are you using Angular? Which version?

And do you use mini-rx/signal-store or the classic RxJS mini-rx-store (with mini-rx-store-ng)?

spierala commented 2 months ago

Quick update:

I can reproduce with mini-rx-store (and mini-rx-store-ng) by simply adding StoreModule.forRoot to the app module (ng18)

Screenshot 2024-09-16 at 21 57 44

However with MiniRx Signal Store there is no issue. It does not have the Singleton check and the corresponding error.

If you are using Angular, is it an option for you to use MiniRx Signal Store?

Here is a small repo with Signal Store in a SSR Angular project: https://github.com/spierala/mini-rx-signal-store-ssr

matejvorcak commented 2 months ago

I am using mini-rx-store in a separate store library, which is consumed by other apps. I prefer to stay framework-agnostic, so migrating to Signal Store is not an option for me. I also think that a singleton check is not enough, since there are still global objects (e.g actions$)that can be mutated by multiple asynchronous requests.

spierala commented 2 months ago

Would it help to remove the error from configureStore?

https://github.com/spierala/mini-rx-store/blob/master/libs/mini-rx-store/src/lib/store.ts

Can you try that? Maybe you can modify the js files in the node module to see if that would help.

If it works, we can think about a Pull Request.

matejvorcak commented 2 months ago

I tried to remove the singleton check from the configureStore function, but the app failed elsewhere:

Error: @mini-rx: configureStore detected reducers. Did you instantiate FeatureStores before calling configureStore? at miniRxError (webpack:///node_modules/mini-rx-store/index.esm.js:54:9) at configureStore$1 (webpack:///node_modules/mini-rx-store/index.esm.js:263:5) at configureStore (webpack:///node_modules/mini-rx-store/index.esm.js:378:5)

I also found that there are many global variables exported from this file:

https://github.com/spierala/mini-rx-store/blob/master/libs/mini-rx-store/src/lib/store-core.ts

For example, reducerState, reducer$, actions$, and appState.

For correct operation on the server side, there should be no global variables. Everything should be tied to a store instance, as there can be multiple store instances at the same time on the server side.

spierala commented 2 months ago

Would it be possible for you to try mini-rx-store@3.1 ?

in that version actions, reducer state, etc were bound to the StoreCore class: https://github.com/spierala/mini-rx-store/blob/3.1.0/projects/mini-rx-store/src/lib/store-core.ts

You would also need to disable the singleton error.

I am curious to hear if that approach works better.

FYI In v4 we refactored the class approach to functions to make mini-rx more tree-shakable.

matejvorcak commented 2 months ago

I tried mini-rx-store@3.1, but even when I disable the singleton check in the Store class (https://github.com/spierala/mini-rx-store/blob/3.1.0/projects/mini-rx-store/src/lib/store.ts), there is still an issue with the StoreCore class (https://github.com/spierala/mini-rx-store/blob/3.1.0/projects/mini-rx-store/src/lib/store-core.ts). It is initialized as soon as it is imported into the app. When I tried to add the "todo" feature twice, it throws this error: Feature "todo" already exists. When I removed the feature existence check, it works. However, there is still a shared actions$ instance for possibly many stores, which could lead to some problems.

I think the best approach is to have multiple isolated stores, each with its own actions$ and features.

spierala commented 2 months ago

Thanks for further investigating!

I think the best approach is to have multiple isolated stores, each with its own actions$ and features.

This indeed seems to be the solution.

However, I am not sure how to implement it without changing the API of mini-rx significantly. E.g. actions$ should be always independent from Store. I want to avoid that devs must use something like Store.actions$ with the "Isolated Stores" approach.

Currently, we do not have the capacity to investigate the SSR story by ourselves. We would need your help. You could fork mini-rx and create a SSR compatible variant. mini-rx-store-ssr :) If you manage to make it work and keep the existing API as much as possible, we can see if we can apply your solution to the main repo. You could also research other SSR compatible state management libraries, to see how they are implemented. Maybe we can learn something for mini-rx.