Yomguithereal / baobab

JavaScript & TypeScript persistent and optionally immutable data tree with cursors.
MIT License
3.15k stars 117 forks source link

Can I pass param to monkey? #375

Open kurdin opened 9 years ago

kurdin commented 9 years ago

Can I pass param to monkey?

tree.set('getBox', Baobab.monkey({
      cursors: {
        boxes: ['boxes']
      },
      get: function(data, id) {
        return data.boxes.filter(function(b) {
          return b.id === id;
        });
      }
}));

I want to get Box with id and put watcher on it

var id = 10;
var box = tree.get('getBox', id);
box.on('update', function(e) {  
        // console.log('Current data:', e.data.currentData);
        // console.log('Previous data:', e.data.previousData);
});
Yomguithereal commented 9 years ago

Hello @kurdin. You cannot pass parameters to a monkey. Monkeys should remain pure reduction of parts of the tree's state. This said, your problem actually has three solutions:

// Using lodash `indexBy`
// https://lodash.com/docs#indexBy

// Using this monkey
monkey({
  cursors: {
    boxes: ['boxes']
  },
  get: function(boxes) {
    return indexBy(boxes, 'id');
  }
});

tree.get('getBoxes', desiredId);
var cursorForSpecificBox = tree.select('boxes', function(box) {
  return box === desiredId;
});

// or using the object notation shorthand
var cursorForSpecificBox = tree.select('boxes', {id: desiredId});

// note that those tricks also work with `get`

I hope this will help you.

kurdin commented 9 years ago

Thanks for examples. Now I see that this can be done in different ways.

Another question about monkey:

from docs:

The dynamic nodes are lazy and won't actually be computed before you get them (plus they will only compute once before they need to change, so if you get the same dynamic node twice, the computation won't rerun).

but when I just define monkey and create Baobab tree, I can see that monkey function is executed. If I change Baobab array with push, monkey function fires again for each item in the array. is it normal, it does not sound like lazy.

Here is the code:

var state = {
      boxes: [  
        {id: 1437507218061, top: 33, left: 155, width: 300, height: 300},
        {id: 1437507218062, top: 33, left: 355, width: 10, height: 400}
      ],
      getBoxIds: Baobab.monkey({
      cursors: {
        boxes: ['boxes']
      },
      get: function(data) {
        console.log('data.boxes in get', data.boxes);
        return data.boxes.map(function(b) {
          console.log('b.id', b.id);
          return b.id;
        });
      }
      })
      };

      var State = new Baobab(state);
      console.log('pre getBoxIds id');
      console.log('State getBoxIds id', State.get('getBoxIds'));
      console.log('post getBoxIds id');
      State.select('boxes').push({id: 1437507218064, top: 33, left: 155, width: 300, height: 300});
      console.log('State another getBoxIds id', State.get('getBoxIds'));

console output

data.boxes in get [Object, Object]
b.id 1437507218061
b.id 1437507218062
data.boxes in get [Object, Object]
pre getBoxIds id
State getBoxIds id [1437507218061, 1437507218062]
post getBoxIds id
data.boxes in get [Object, Object, Object]
b.id 1437507218061
b.id 1437507218062
b.id 1437507218064
data.boxes in get [Object, Object, Object]
State getBoxIds id 2 [1437507218061, 1437507218062, 1437507218064]

Another separate problem I have with sort inside monkey. If I add monkey sortById

      sortById: Baobab.monkey({
      cursors: {
        boxes: ['boxes']
      },
      get: function(data) {
        console.log('data.boxes in get', data.boxes);
        return data.boxes.sort(function(a, b) {
            var x = a['id']; var y = b['id'];
            return ((x < y) ? -1 : ((x > y) ? 1 : 0));
        });
        }
      })
      };

I got error: Uncaught TypeError: Cannot assign to read only property '1' of [object Array] data.boxes inside monkey get seem to be a normal array, I don't understand why sort function does not work with it. Is not monkey a good place for creating different sort methods?

Yomguithereal commented 9 years ago

Hello @kurdin. Let me check this more thoroughly. Concerning your last question: the thing is the tree's data is immutable by default and the Array.prototype.sort in JS actually mutates the array. Hence the error thrown.

kurdin commented 9 years ago

@Yomguithereal I realised about immutable array after couple of tests. I think it would be very useful if .get() method can return normal array that you can change, or maybe implement something like toJS() . If everything is immutable in would be difficult to do any type of sorting. I guess workaround for now is to disable immutable tree.

Yomguithereal commented 9 years ago

We are discussing the addition of such methods in #380.

Zache commented 8 years ago

@kurdin I like using the spread-operators when immutability is preventing/helping me from doing something.

let sortedNumbers = [...tree.get('numbers')].sort((a,b) => a-b)

Looks neat to me, the same can be done for objects with the help of experimental features

let renamed = { name: 'default name', ...tree.get('person'), name: 'Zache' };

The biggest downside probably being extra GC pressure?

Yomguithereal commented 8 years ago

That's the exact same downside with cloning. But you cannot really get around it anyway.