Closed ai closed 3 years ago
Name fixing plan:
create*
and define*
prefixesSolution for “People are not ready that some store can lost its value” problem:
on
& off
for first listener subscribing and 1 second after last listener unsubscribingQuestions:
$
to store names? It will simplify map building and finding an argument name for $profile.listen(profile => …)
callback.People are not ready that some store can lost its value
I like to know that the store is cleaning up after itself. Sometimes it can be useful. Since this is “nano” package, we can make something like “temp store”.
create and defined method prefix makes name too long
I like prefixes. They make API much more explicit. We can abbreviate all names to “a”, “b”, “c”. But why?
should we merge projects?
I made PR with Vue binding for dotto.x. It's interesting, but it has poor tests and it could be cleaner.
Store → Atom
😑
Should we add $ to store names?
No. I've been writing code with “$” in Vue 2 for several years. With the release of Vue 3, where “$” is no longer needed, I have become a little happier.
Should we add “action” concept?
No, it also seems to be an outdated and inconvenient feature. (Vue 2 flashbacks) Give an example, perhaps I misunderstood.
I like to know that the store is cleaning up after itself. Sometimes it can be useful. Since this is “nano” package, we can make something like “temp store”.
My current idea is to make explicit cleaning in off
event (last subscriber unsubscribed).
All our stores (Logux’s SyncMap
, persistence store, router) will clean itself.
We can add autoclean(store)
helper.
I like prefixes. They make API much more explicit.
I liked them too. But even in persistence, store methods became too long.
Svelte and Recoil use prefix-less names and API examples look cleaner.
But, Effector has prefixes.
@AleksandrSl what do you think?
Store → Atom 😑
We need some solution. You can suggest a better one with:
No. I've been writing code with “$” in Vue 2 for several years. With the release of Vue 3, where “$” is no longer needed, I have become a little happier.
touchér
No, it also seems to be an outdated and inconvenient feature. (Vue 2 flashbacks). Give an example, perhaps I misunderstood.
It is just an optional syntax sugar:
Old:
function addUser (newUser) {
if (validate(newUser)) {
update(usersStore, users => users.concat([newUser]))
}
}
New:
const addUser = action('addUser', newUser => {
if (validate(newUser)) {
update(usersStore, users => users.concat([newUser]))
}
})
Action can be useful for better DevTools to show action names in events instead of store X was changed
.
Answers:
store
-> atom
. As I already mentioned atom is a too ambiguous name with plenty of meanings, doesn't help to understand what's going on. There I also mentioned that if the meaning behind atom
is that store has a single value, then name this store just like that -> single
/singleValue
. video$
. Overall it looks like Hungarian notation, it has many pros/cons, I think it's a matter of taste. Don't see so much difference between projectStore
, project$
. Though store
is a short and simple word (compared to observable) so I would incline to the verbose projectStore
variant. We can write some eslint plugin as effector recently did to suggest the right naming. Names consistency is much more important than names themselves sometimes.Questions:
It will simplify map building
How $ will simplify building?
How $ will simplify building?
Our JS files parser will automatically detect stores by simple variable name RegExp
Hello everyone! I would like to join the discussion with the following suggestions:
This abstraction gives the ability to enhance your stores and keep them very small. I was able to reduce the size to 135B. With Lifecycle, we can write plugins like Persistent, Undo/Redo, CRDT sync, Loggers/Devtools, etc. Users can pick useful plugins for tasks and combine them for personal needs.
What is implemented in Dotto.x and what I would like to propose for implementation in Nanostores:
This abstraction provides listening to pinpoint changes and set some values by dots (like lodash). This is the reason why the state manager is named Dotto.x. “Dotto” is “dot” in Japanese.
It is the same as “derived” but resolves the diamond problem. The core of this solution is to listen to writable stores only. Here is my previous realization without “take” and “deep” operators. https://github.com/dottostack/dotto.x/blob/feat/effects/operators/computed/index.js#L42
It is a very useful functionality and we can implement it with Lifecycle.
This solution is simpler for testing than different types of stores because we can use all plugins in the integration layer and write tests only for stores.
I’m not a full-time developer, so I read the code more as a designer. Because of the long breaks between design and code, I often have to remember many things from scratch.
Actions and anything that helps make devtools and logging better is cool.
Lifecycle
What is a lifecycle? Can you describe it a little more?
Deep Stores
Sure. What is requirements for that?
The core of this solution is to listen to writable stores only
Can we listen for other computed stores?
Undo/Redo plugin
Added to the list
Persistent store as a plugin
We have https://github.com/nanostores/persistent
What we can add there?
I hear all reasons against atom
, but for now, I think it is a better term:
Long names and prefixes are ok and even good. Readability is the ability to use the library and work with it without learning API.
I removed “create*
removing” task from the list 👍
Can we listen for other computed stores?
https://github.com/nanostores/nanostores/pull/58
I'm going to answer other questions a little bit later
A good reason for action(cb)
wrapper for store changing functions: we can force using effect
in action
’s callback is async
function (return Promise
).
Or just wrap action
callback to effect
if it returns Promise
.
What is a lifecycle? Can you describe it a little more?
Lifecycle is a module for wrapping the base store and providing these features:
In Nanostores we can use different types of stores. What if we would use Smart + Persistent? Or Smart + Persistent + Shared (with CRDT) at the same time and keep small size?
How it can look:
import { createStore } from "nanostores";
// step 1
// simple pure store
const projects = createStore({});
import { createStore } from "nanostores";
import { mount } from "@nanostores/plugins";
// step 1
// simple pure store
const projects = createStore({});
// step 2
// store with constructor and destructor
mount(projects, () => {
projects.set({ name: "someProject" });
return () => projects.set({});
});
import { createStore } from "nanostores";
import { mount, persistent } from "@nanostores/plugins";
// step 1
// simple pure store
const projects = createStore({});
// step 2
// store with constructor and destructor
mount(projects, () => {
projects.set({ name: "someProject" });
return () => projects.set({});
});
// step 3
// store keeping state
persistent(projects);
import { createStore } from "nanostores";
import { mount, persistent, crdt } from "@nanostores/plugins";
// step 1
// simple pure store
const projects = createStore({});
// step 2
// store with constructor and destructor
mount(projects, () => {
projects.set({ name: "someProject" });
return () => projects.set({});
});
// step 3
// store keeps state
persistent(projects);
// step 4
// state will be synchronized
crdt(projects, someConfig);
This idea provides enhanced stores but keep threeshakable and friendly for all users because they can decide what they really want and get it. Integration of a new feature in the existing project requires 2 lines of code only.
In Nanostores we are patching base methods like the set in the Persistent store. But this is not scalable because if we start wrapping stores dynamically we can break chains of decoration. Dynamic wrapping is to wrap some method of stores by “dispose” (unsub) principle.
It’s something like middleware in Redux with steroids. We can share data between all life cycles.
All users can write plugins for Nanostores and improve our ecosystem.
Seems like we still need to remove create…
prefix from names:
createMapTemplate()
looks too long. We will have many 3 words functions in API.createMap
name will help us with backward compatibility. map()
will use new lifecycle, createMap()
will use old auto-cleaning.@Eddort I added an issue about lifecycle feature https://github.com/nanostores/nanostores/issues/59 Let’s discuss details there
Seems like I will start DevTools from logger and in 0.5 release we will just have API to build DevTools extension.
I like the design of Effector Logger https://github.com/effector/logger
What API we need for logger:
onCreate(({ store }) => …)
event?Events: 𝖓 action
, 𝖓 change
, 𝖓 abort
, 𝖓 start
, 𝖓 stop
, 𝖓 clean
(we discussed with @euaaaio to use white 𝖓 in block box as an icon).
What else?
If you still need opinions regarding the API, I find subscribe/listen misleading. To me it is an observable and I would propose observe
and observeNow
as a replacement
subscribe
refers to pub-sub pattern, and feels like it is missing a channel.
Same for store.set
it also has a key/value feeling, maybe store.next
would work better.
@droganov we need Svelte compatibility. This is why subscribe
has this behavior. subscribe
and set
are part of Svelte store API.
| Derived → Computed
combineStores?
combineStores
One source store is also a very popular use case for computed stores
One source store is also a very popular use case for computed stores
OK, I see... A kinda new concept, a store that includes some selector or reducer, looks like a projection, but it looks you can call set method.
New lifecycle events system was moved from dotto.x
by @Eddort https://github.com/nanostores/nanostores/pull/62
I cleaned up a little it https://github.com/nanostores/nanostores/pull/64
Pure JS API to listen specific keys was added 7faec35
import { mount } from "@nanostores/plugins";
What if we name it lazy
instead of mount
? Mount creates expectation that a store will be mounted immediately after calling mount.
@droganov we renamed mount
to onMount(store, cb)
I moved framework integrations to separated npm and GitHub projects in @nanostores
org.
We finished main changes of 0.5. Now I will finish docs and @Eddort is planning to create logger to be sure that new API works fine.
Router, Persistence Store and Logux Client were ported to Nano Stores 0.5 next
and works great.
Seems like 0.5 is ready for release in next few days.
We collected first feedback of Nano Stores, and we are ready for next big refactoring and fix user’s complaints.
We will try to keep backward compatibility: we will wrap new methods to old methods and print warning.
Refactoring goals:
defaineMap
) is not self-describablecreate*
prefixaction()
builder witheffect
auto-wrapperdotto.x by @Eddort solve most of these problems and can be a huge inspiration source (should we merge projects?).
Changes after release:
filename:line