arqex / freezer

A tree data structure that emits events on updates, even if the modification is triggered by one of the leaves, making it easier to think in a reactive way.
MIT License
1.28k stars 56 forks source link

What changed #71

Closed bep closed 8 years ago

bep commented 8 years ago

This is related to #17 -- but it really deserves a fresh discussion. Freezer is a great tool, and it almost replaces the extremely complex mental model of all the fluxes or whatever.

As said in #17, I agree that a general diff support would kill performance, but some support on top of the Freezer object should be possible.

In most cases, the application state object may be very fine-grained (and a mix of temporary (status messages etc.) and permanent state), but the updates will typically be very coarse-grained.

So, given this imaginary state object:

status: 'ready',
searchFilter: {...},
userRoles: [],
user: { 
  profile: {},
  orders: []
}

So for updates I'm only interested in events from:

So, if you create some kind of wrapper that keeps a history of state changes, it should be possible to listen for these updates only by diffing only these two objects. And then maybe add undo/redo as an add-on.

arqex commented 8 years ago

Hi @bep

From my experience, the most you try to make you render updates more selective (like only re-render when user.profile has changed) the hardest is to keep the control of your application.

Freezer paradigm is based on re-render everything when something changes, it may sound inefficient, but believe me, I have never had performance issues with this approach even with applications that mutates the data so frequently as a svg editor (dragging and dropping multiple elements with real-time ui feedback).

If you need to detect changes inside some node, you can still create a listener in it, and subscribe to its changes:

var store = new Freezer({user:{profile:{}, orders:[]}});

var listener = store.get().user.profile.getListener();
listener.on('update' () => console.log( "The profile changed" ) );

I only would use this approach in case that I have to update something outside the react world, and I don't get the props populated automatically by the main re-render call.

But, the main reason of freezer not offering info of what has changed is that you can have the same data repeated in multiple places of the state. For example:

var store = new Freezer({
 users: [{id: 1, name: 'John'}, {id: 2, name: 'Alice'}],
 currentUser: false
});

var state = store.get();

state.set({currenUser: state.users[0]});

// Now john is repeated inside the state
// This will change state.currentUser.name and state.users[0].name
store.get().currentUser.set({name: 'Bill'});

console.log( store.get() );
/*
{
 users: [{id: 1, name: 'Bill'}, {id: 2, name: 'Alice'}],
 currentUser: {id: 1, name: 'Bill'}
}
*/

One update can change multiple parts of the store, so it's really hard and expensive to check what's changed.

bep commented 8 years ago

var listener = store.get().user.profile.getListener(); listener.on('update' () => console.log( "The profile changed" ) );

Hmm... Pretty sure I tested this ... OK, nevermind, you are right.