4

I'm getting into ReactJS and am intrigued but also confused about persistent data structures. I love the idea, but I'm not sure how to take my MV*, Mutable, Observable Bindings experience in designing view components and apply it in a sane way.

For example, suppose I have a deeply nested structure:

Foo
  Bar
     Baz
      someValue
  Qux
  Quxx

In my UI, I have a component that is an editor for someValue. In a mutate-observe paradigm I would do something like:

Baz.setSomeValue(newValue)  // trigger observers, etc...

As near as I can tell, though, the equivalent with persistent data structures is something like:

Foo = extend(Foo, { Bar:{ Baz:{ someValue: newValue } } });
// recompute with new value of Foo

What is the normal pattern for encapsulating a component that deals with Baz so that it doesn't have to know the entire structure?

4
  • Define abstractions just like setSomeValue but instead of mutating it in place, efficiently create a new copy. Apr 1, 2014 at 13:38
  • 9
    My first impression is you need functional lenses. Aug 25, 2014 at 19:56
  • Read this Prismatic blog post
    – paul
    Nov 14, 2014 at 20:32
  • Take a look at the idea of "cursor" from this article. It's not explained in detail there, though, only the idea is shown. Basically It is similar to a lens, but with a more traditional interface :)
    – 9000
    Jun 13, 2015 at 1:00

2 Answers 2

1

What you are searching for is called a "lens" - it is a function which gets or sets a property of a nested object and outputs a new version of the whole object.

There are some good lens libraries in JS, and after you are familiar with the concept it is not hard to implement it yourself.

0

Part of your trouble is that you're using JavaScript and not a functional programming language like ClojureScript. In ClojureScript you'd have the aid of atoms and update-in and assoc-in to help with these nested structural updates. Languages that don't emphasize doing everything with persistent data structures aren't optimized for this use case.

However, that said, there is another way. One thing I've taken to is modeling complex objects (anything made up of key/value pairs) as entities. Each object upon creation would be assigned an immutable ID (I used BSON IDs but GUIDs will do). Then in this manner you're able to eliminate all nesting:

function Entities(state){
  this.state = state || {}; //never mutated
}
Entities.prototype.update = function(id, immutableData){...};
Entities.prototype.get = function(id){...};

function Ref(id){
  this.id = id; //never mutated
}

var entities = new Entities();
entities = entities.update(1, {id: 1, name: 'Foo'});
entities = entities.update(2, {id: 2, name: 'Bar', children: [new Ref(1)]});
entities = entities.udpate(3, {id: 3, name: 'Baz', children: [new Ref(2)]});
entities = entities.update(3, entities.get(3).set('name', "Bazooka"))

The update method doesn't mutate, but rather returns a new representation of the modified entity state object;

By using Reference objects, you've effectually eliminated the nesting altogether. You could use the actual integers instead of Refs; however, that doesn't make it clear that what you have in your children array is actually a reference to some other entity. Wrapping it effectively transforms the integer into a Ref type. It's up to you to interpret the ref type as a pointer to the entity.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.