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?

noah
  • 141
  • 3
  • Define abstractions just like `setSomeValue` but instead of mutating it in place, efficiently create a new copy. – daniel gratzer Apr 01 '14 at 13:38
  • 9
    My first impression is you need functional lenses. – Mario T. Lanza Aug 25 '14 at 19:56
  • Read this [Prismatic blog post](http://blog.getprismatic.com/om-sweet-om-high-functional-frontend-engineering-with-clojurescript-and-react/) – paul Nov 14 '14 at 20:32
  • Take a look at the idea of "cursor" from [this article](http://omniscientjs.github.io/guides/01-simpler-ui-reasoning-with-unidirectional/). 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 '15 at 01:00

2 Answers2

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.

Jencel
  • 217
  • 1
  • 9
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.

Mario T. Lanza
  • 1,700
  • 1
  • 13
  • 22
  • [A related thread](http://programmers.stackexchange.com/questions/208154/everything-is-a-map-am-i-doing-this-right) – paul Nov 17 '14 at 18:07