You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Vanilla state management is difficult in vella because S isn't intuitive.
Vella benefits from granular stream updates
Due to 2. single state objects compromise Vella's performance value proposition
Any state management approach we use must be optimized for 2.
Solutions
We should borrow from some of my research on queries+streams.
We can heavily simplify the API for the specific use case of state management within vella.
Query+Streams uses proxy property access so it should feel more "native", and it can be typescript compatible in a similar way to monolite
Examples
// v.boot will provide access to a state query streamv.boot(document.body,({ state })=>{// you can read the state by invoking it:console.log(state())// you can write to the state by invoking it with a parameterstate(newState)// you can transform the state by passing in a transform functionstate(prevState=>f(prevState))// you can access sub properties of the state with normal property accessconstfirstUser=state.users[0]// `firstUser` has the exact same api as `state`console.log(firstUser())firstUser(newUser)firstUser(prevUser=>f(prevUser)// Both `state` and `firstUser` are S streams that vella natively understands// But `firstUser` only "emits" when its value changes// this will only re-render when firstUser.name changes value.returnv('p','Hello',firstUser.name)})
If state wasn't already provided as an attribute, every component will receive state as an attr. The default reference will be the state the parent component received.
functionMyComponent({ state }){// And state can be rebound to a subtree// as easily as aliasing it in the attrsreturnv(OtherComponent,{state: state.subsection})}
state instances will have a few methods for relational queries.
map for mapping over lists
find for selecting an item in a list
delete for removing an item from a list
These methods are different from attain queries, where operations focus on the query not the underlying value. But vella doesn't need to be so puritanical because it has a specific use case in mind - so it can optimize the api to feel more like working with native JS structures.
An example of .find
// bad - points to a specified index which is brittleconstuser=state.users[0]// good - a dynamic query which focuses // on a specific identity relationship - which is resilientconstuser=state.users.find(user=>user.id==id())
And .delete()
constuser=state.users.find(user=>user.id==id())// remove from list/object where user.id == id()user.delete()
A simple counter example (excuse any typos):
v.boot(document.body,({ state })=>{// initialize the statestate({counters: []})// mount Counters without passing down statereturnv(Counters)})// Counters receives the root state as an attr// automaticallyfunctionCounters({ state }){returnv('.counters',v('button',// create a counter on click{onclick: ()=>state.counters(xs=>xs.concat({id: uuid(),count: 0}))},'Add Counter')// map over the counters (.map here produces a Stream<UUID[]> ),v.list(()=>state.counters.map(x=>x.id),id=>// create a query on the fly for each componentv(Counter,{state: state.counters.find(x=>x.id==id)})))}// state is focused on the counter query functionCounter({ state }){returnv('counter'// because of transform functions // we don't have that awkward count( count() + 1 )// and therefore we can take advantage of most utility toolbelts,v('button',{onclick: ()=>state.count(x=>x+1),'+'},v('button',{onclick: ()=>state.count(x=>x-1),'-'}// Only mutates the dom when // `state.users.find( x => x.id == id ).count` changes,v('p.count','Count',state.count),v(Delete))}// receives the counter as a state query// even that no attr was specifically passed downfunctionDelete({ state }){// Remove the counter from the listreturnv('button',{onclick: ()=>state.delete(),'Delete'}}
The text was updated successfully, but these errors were encountered:
@JAForbes this seems like a tremendously great approach; almost too good to be true! I'd love to have it in an alpha branch sooner than later. I'm curious what the accompanying persistence idioms would/should look like.
Yeah seems like there's a bit of support (even @barneycarroll has come around to it 😂). I'll try and find the time to implement a prototype if there's no objections.
Assumptions
Solutions
Examples
If
state
wasn't already provided as an attribute, every component will receivestate
as an attr. The default reference will be the state the parent component received.state
instances will have a few methods for relational queries.map
for mapping over listsfind
for selecting an item in a listdelete
for removing an item from a listThese methods are different from attain queries, where operations focus on the query not the underlying value. But vella doesn't need to be so puritanical because it has a specific use case in mind - so it can optimize the api to feel more like working with native JS structures.
An example of
.find
And
.delete()
A simple counter example (excuse any typos):
The text was updated successfully, but these errors were encountered: