> Our actual solution is to store the state in our server-side database. This feels wrong at first glance: we're storing a client-side React component's state as an opaque JSON blob in our server-side database, and we're doing that knowing that some of those JSON blobs will go out of date and be unusable in the future. But io-ts and the type system keep us honest here! And this is a convenience feature, so we can always change it later, even if that means throwing away all of the saved states. (When a user finishes a lesson, that record is stored in a separate part of the database.)
That seems very... bold. That said, I could see this being useful for more ephemeral situations where it's okay to not be entirely in sync with the server. Like the user signup example: multi-step forms, or some create-only or override-always sort of arrangement, or maybe some filters for some sorting/slicing data tables, etc.
It's then useful for not being jittery on load + not storing half-baked data just to have semi-persistence of incomplete forms. Quickly rendering existing state is always a win, without resetting it then loading the updated position.
Otherwise I could see this introducing a lot of race conditions and additional work being done post-load to clean up the data.
Since there's a relatively short line here where it just makes sense to rebuild the state, it's probably preferable to utilize this for smaller isolated parts of the site, like a single form's data, instead of the wider state. But still an interesting approach none-the-less, made easier with the reducer pattern...
We only use it for resuming partially-finished lessons, so we're using it in basically the way that you describe. Overall course progress is tracked in a totally different way.
Exactly, the ideal solution is the one they dismiss first.
> Solution 1: Store the current step index
Every issue they mention can be solved by assigning each step a UUID and doing proper database migrations when deleting a step so people get reassigned to a neighboring step.
>1. Why marry the state stored in the database with any particular implementation of the front-end?
They seem to have only one front-end implementation to worry about for now, and the client state is most likely pretty generic (they don't really make this clear, but I can't think of anything react-specific you'd want to store in a reducer state).
We did the same at a previous company. This was Redux, so maybe a little different, but it went very smoothly.
We stored a subset of the redux store locally, and a further subset of that was sent to the server. This data is quite prone to changing, so a JSON field in the database is fine. You're not going to do any advanced querying / migrations on it.
Restoring was pretty easy:
- Try to grab from local storage
- Try to read from remote
- Merge those (let local overwrite remote)
- Pass the merged object through a migration function
- Pass the migrated data to `createStore`
The biggest pain point was dealing with TypeScript and migrations. The app only knows if the most recent store interface, so migration functions looked a lot like "old" javascript (`if (foo && foo.bar)`, `typeof foo === 'string'`, that sort of stuff).
Easy persistence / restoring is one of the many things I like about Redux.
You can express a type as one of many interfaces, or use discriminated unions (https://www.typescriptlang.org/docs/handbook/advanced-types....) by using some unique version key to identify each interface. Then you'll have all the goodness of types back :)
[+] [-] dmix|6 years ago|reply
That seems very... bold. That said, I could see this being useful for more ephemeral situations where it's okay to not be entirely in sync with the server. Like the user signup example: multi-step forms, or some create-only or override-always sort of arrangement, or maybe some filters for some sorting/slicing data tables, etc.
It's then useful for not being jittery on load + not storing half-baked data just to have semi-persistence of incomplete forms. Quickly rendering existing state is always a win, without resetting it then loading the updated position.
Otherwise I could see this introducing a lot of race conditions and additional work being done post-load to clean up the data.
Since there's a relatively short line here where it just makes sense to rebuild the state, it's probably preferable to utilize this for smaller isolated parts of the site, like a single form's data, instead of the wider state. But still an interesting approach none-the-less, made easier with the reducer pattern...
[+] [-] gary_bernhardt|6 years ago|reply
[+] [-] sdegutis|6 years ago|reply
It's just traditional server-side sessions. Just done more complicatedly to work around React's architecture.
[+] [-] Tade0|6 years ago|reply
1. Why marry the state stored in the database with any particular implementation of the front-end?
2. If the problem is in the ever changing state schema, why not create migrations for it?
[+] [-] StevenWaterman|6 years ago|reply
> Solution 1: Store the current step index
Every issue they mention can be solved by assigning each step a UUID and doing proper database migrations when deleting a step so people get reassigned to a neighboring step.
[+] [-] uhoh-itsmaciek|6 years ago|reply
They seem to have only one front-end implementation to worry about for now, and the client state is most likely pretty generic (they don't really make this clear, but I can't think of anything react-specific you'd want to store in a reducer state).
[+] [-] mikewhy|6 years ago|reply
We stored a subset of the redux store locally, and a further subset of that was sent to the server. This data is quite prone to changing, so a JSON field in the database is fine. You're not going to do any advanced querying / migrations on it.
Restoring was pretty easy:
- Try to grab from local storage
- Try to read from remote
- Merge those (let local overwrite remote)
- Pass the merged object through a migration function
- Pass the migrated data to `createStore`
The biggest pain point was dealing with TypeScript and migrations. The app only knows if the most recent store interface, so migration functions looked a lot like "old" javascript (`if (foo && foo.bar)`, `typeof foo === 'string'`, that sort of stuff).
Easy persistence / restoring is one of the many things I like about Redux.
[+] [-] bitten|6 years ago|reply