-
-
Notifications
You must be signed in to change notification settings - Fork 106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Nanostores in SSR #80
Comments
Am I right that you are using the same Node.js context between different users during the async SSR? I'm afraid that state manager isolation is a weak security strategy for this case. The data of one user can be leaked via many global structures like caches in npm libraries, etc. Is it possible to create the new global context between these calls? |
Short answer to the direct question:
This way can be used with Nano Stores without any changes. Just move stores from files like |
I'm not using SSR at all right now, but it is a common problem with Next.js (if it is used as a classic Node.js server or something that is not serverless), so i was interested, how it works with nanostores
But, if i'm not missing something, this way |
Got it. Yeap, it will be a problem here. You need to use explicit store loading Promise like |
Hi there. And what about hydration/dehydration/snapshoting to transfer the state from the server to the client? Are there any plans for a full SSR support? |
You can do it by store.set(initialState) To do it automatically, you need a framework like Next.js or Svelte Kit, some wrap on top of state manager. Maybe you want to maintain this project? Do you know a good examples around other state managers? |
Nanostores doesn't have a root state. Just use |
Thanks for answers.
Unfortunately, I do not know state managers with atomic stores, which can be given as an example. As for the |
Just an idea. We can extend Vue integration for SSR with |
I think, nanostores could utilize similiar stack-based approach like the one in General idea is something like this // internal field
let scope = null
const makeStore = (name, initState) => ({
// all stores will need an unique name or identifier, which should be the stable between server and client
name,
state: initState,
getState() {
// if scope is set during operation, use state from scope
if (scope) {
return scope[name] ?? initState
}
return this.state;
},
set(v) {
if (scope) {
scope[name] = v;
return;
}
this.state = v;
}
})
// scope fabric
const getScope = () => ({});
// general pattern for working with scope
// pass target scope explicitly
const runInScope = (s, cb) => {
// set it to internal field
scope = s;
// run logic
cb()
// thanks to callstack all store api calls will see
// which scope is set now
// but requires additional work to handle async stuff
// clear internal field
scope = null;
}
// in the app code
const myScope = getScope()
runInScope(myScope, () => {
store.set("foo")
})
myScope[store.name] // "foo"
// all synchronous reactions will also be "in the scope" automatically
myScope[someDeeplyDerivedStore.name] // "derived from foo"
// but it is still needed to somehow preserve scope for any async operations or effects This would allow to automatically collect all the states during SSR and then transfer it to the client: // ssr
const myServerScope = getScope()
// ... run some logic on scope
const stringifiedStateOfTheApp = JSON.stringify(myServerScope)
// client
const myClientScope = JSON.parse(stringifiedStateOfTheApp) And also separate state of different requests/tests: const myTestOrSsrRequestHandler = () => {
// each test/request gets its own isolated box for app state
const myScope = getScope()
...
} This is very roughly how effector scopes work, but advantages are the same: effector is able to both isolate and automatically collect all atomic stores states without any manual boilerplate |
Yeap, Effector’s Maybe we can create some sort of the wrap around current minimalistic stores to support scope? |
I don't think that wrapper would work - if user missed some store, then it will be missed from serialization and further hydration at the client, which is no good, if it, for example, was changed during SSR (on the other hand, it may make sense to ignore some stores in serialization, for e.g. internal stores of libraries) Probably it should be a core feature, so all APIs of the library are aware of the scopes and can properly handle it 🤔 I don't think that it is actually matter, if server context is isolated or not - the problem of collecting all parts of decentralized state of the app is still persists, since it is needed to hydrate app state at the client somehow anyway 🤔 |
I have another solution, but the documentation is very weak. https://github.com/Eddort/nanostores-ssr The main idea is to create a handler for each request (SSR function) and create a new context for each new request. |
Hm, could we pass |
Any updates on using SSR? The documentation is very short and I don't get enough clarity. My use case is to be able to hydrate the store from a Server Component in React 18 / Next.js. The underlying reason is that by hydrating I hope to avoid an extra re-render. Thanks! |
I don’t use Next.js and we need some advanced user to do the research and optimization |
Hi there!
I saw news on new release, saw
allTasks
API and decided to research, hownanostores
is compatible with SSRLoading of Node.js template in codesandbox takes forever for some reason, so i decided to create similiar reproduce with basic one:
https://codesandbox.io/s/loving-wilson-4vp4r?file=/src/index.js
(see
index.js
and console)Idea is the same:
During all of these operations is important to isolate state that "belongs" to different requests from each other - otherwise data from one request will leak to the other
Nanostores have two issues there:
States of nanostores are shared between requests, which then leads to data prepared for one request to show in another - in result rendered html-string is not correct.
State of internal
allTasks
counter is also shared between all requests, which leads to weird bug: responses are not sent until all requests are handled, even if their data is ready.Possible solutions:
This will effectively isolate requests from each other, because all of them will have their own instance of stores and all pending operations also will be bounded to this instance
Mobx example: https://github.com/vercel/next.js/tree/master/examples/with-mobx-state-tree
effector
, allow to create separate instance of app state in special way, allowing to reuse already initialized stores and connections, like this:Example: https://github.com/GTOsss/ssr-effector-next-example/blob/effector-react-form-ssr/src/pages/ssr.tsx
The text was updated successfully, but these errors were encountered: