Hacker News new | comments | show | ask | jobs | submit login
React-cursor: a JavaScript implementation of the Cursor concept first seen in Om (github.com)
136 points by dustingetz on Sept 23, 2014 | hide | past | web | favorite | 68 comments



(Author here) I have added a short section to the readme describing how `react-cursor` differs from other, similar, libraries (e.g. Cortex). https://github.com/dustingetz/react-cursor#comparisons-to-si...


As of a few weeks ago my Cursors implementation correctly `shouldComponentUpdate`s path changes as well as value changes ;)


The comment on the double setState issue on Github just cleared up a point of confusion for me:

> It might be best to think of setState as being enqueueStateUpdate

Is this why the cursor objects have something like a "pendingState" property? I can't remember the exact name, just that it exists.


That is correct. `cursor.value` always respects the react component lifecycle: this is what you always want in `render()`, `componentWillUpdate()` and other React lifecycle methods.

`cursor.pendingValue()` gives you the latest queued state update, which is always what you want in your event handlers (e.g. complicated state transitions in response to a click).

The decision is essential complexity, but at least when you use cursors the decision is mechanical. React has a slightly different API (but equivalent information) that makes the decision less mechanical.


Thanks for the comparison. And good catch with the cursor equality relying on both value and path. That is something that was recently fixed in Om.


If you are tired of using callbacks when nesting data structures with React, be sure to check out Cortex: https://github.com/mquan/cortex

Cortex's goal is to support arbitrarily deep data structure without requiring you to pass callbacks down the chain.


What I need is something like this that abstracts away where data is coming from (i.e. facade pattern). This would help make implementing offline capability seamless, and a discrete concern from the UI.


Indeed. I spent a bunch of time looking for something like that which already exists, but couldn't find anything and have resigned to building it myself. When it gets to a better state I'll be releasing it. Probably in a week or so.


+1 on being interested... I'm beginning the same journey here: https://github.com/natew/testreactor


Can you contact me via email? In my profile.


I would like to see this as well.


looking forward!


I'm new to the React world but this sounds a bit like what Flux (http://facebook.github.io/react/docs/flux-overview.html) is trying to acheive. Does anyone know how these libraries (either cursor or cortex) could tie into that architecture?


To me it feels like they are both trying to solve the problem of modifying state in a react app. Flux is probably more manual, by explicitly creating actions that modify state in a store that is listened to by components. With these cursor type solutions you pass down pieces of state through the component hierarchy (not outside of it as in flux's case), mutating the data as needed. Maybe it's useful to think about flux as being action centric, and cursors data centric? I'm still wrapping my head around Flux and am not entirely convinced its a good thing yet.

I'd love some input from users of om or one of the pure js solutions how it works for them. For example, how would one go about monitoring state outside of a component? Think submitting analytics events when a user does something.


Apologies for my naïveté but what you're saying is that you would either use Flux or some cursor based solution, but they wouldn't work together? I'm still getting into this but I'm very curious as I'd like to architect something robust and it seems this piece of React apps is very much in... flux. What are your thoughts on future-proof and ease of use for each solution?


I'm not really concerned about React-specific implementation, but rather a data structure that serves as a facade over the storage layer (either a remote server or the storage systems made available to the browser). This way the UI (e.g. React components and other view-like things) just talks to this facade and doesn't have to know anything about the network, its availability, or browser-specific storage options.

Mozilla's localForage is a good tool specifically for abstracting away the client-side storage mechanism from the UI logic. Aside from that I have looked at Flux and its stores would be the place where you would implement such a facade.


I've used PouchDB this way in the past, and it worked well for seamlessly enabling offline use/hiding network concerns from the view layer, but it's probably a lot "thicker" than what you have in mind (and isn't completely browser-proof, and requires the server storage to look like a CouchDB).

It's interesting to imagine what the lighter facade you're describing might look like. I guess you'd want the controllers/views to get a key-value style interface, and you'd want the facade to connect with one or more local storage options, and some sort of logic to manage triage between local and remote storage, and syncing between the two, and a well-defined API for connecting the remote storage. Hmmm...


Well, Fb gave a presentation that said interaction with their web utils (so storage if you want to call it that) actually happens in the action itself, with pending, success and failure "actions" (events really) being dispatches to the stores. Not sure what that means for your facade, but it's confusing the hell out of me. What is the store supposed to do? Just hold onto the state, period?


not sure if i am following you, but in my apps, the data is always in React state. Whether it came from network or from localstorage is irrelevant. There is some code somewhere that loads data into React state from whatever source. All react is doing is rendering views as a fn of state.


Sounds like a legit feature request to add to the issues there!


Seems kind of out-of-scope for Cortex. But the notion in general is something I yearn for mightily.


This looks really nice. I'm actually really happy to see the json editor, been thinking about including something along those lines for debugging/interactive discovery in my react apps.

One thing I'm a little confused about is the onChange method. Is that supposed to be like Om's transact? If so, that seems like an unfortunate name. onChange sounds like a callback, not a way to create a new state.


(update) The discussion here has convinced me; I have opened an issue to eventually change the nomenclature.


The most common use of cursors will eventually bind them to React form components. Cursor's value/onChange interface is meant to directly line up with React form components' value/onChange convention. That said, I get asked this question a lot, perhaps mostly by people who aren't already React developers.


Coming from someone who has used React.js a lot, I found the name unintuitive as well. Would've expected a variant on setState. That said, this library looks extremely useful and I can't wait to try it out in a project -- thanks for publishing it!


onChange is a synthetic event which is part of React (and also used in Om of course). http://facebook.github.io/react/docs/events.html


I know about onChange for events on elements. But what exactly is it doing on the cursor? onChange in react allows you to provide a callback when things change. But that doesn't appear to be what is happening with the code samples in the read me.

For example

cursor.refine('b').refine('foo').onChange({ 'bar': 43, baz: 56 })

What is this doing? If onChange isn't transact, what is the react-cursor equivalent?


I agree, the naming is odd. `onChange` is almost always the name of a function that is called by a user event and passed an Event object, not called by the programmer and passed a raw value. Is it supposed to be used as the first line in a form onChange handler? Perhaps something like

    onChange: function(e) { this.cursor.onChange(e.value);}
Even in that case, I think it is a lot more intuitive to use a function name like `change`, `mutate`, or `transact`. Personally, `onChange` is something I want to assign, not something I want to call. Additionally, it doesn't look like it can be passed a raw Event object, so you can't shorthand it in a render method and avoid creating an onChange callback.


I agree. The first minute I looked through the tutorial `onChange` confused me, because I expected it to take a callback.

I would prefer `transact` or any other of your proposed method names over onChange.


Oh, huh. Good question. That does seem a little strange.


Facebook's Immutable also provides Cursors for its immutable collections: https://github.com/facebook/immutable-js#cursors


I found immutable-js's cursors to be rather limited for use with react. Using them is very verbose, and you often end up nesting 2 or 3 little anonymous functions when doing mutations.

I ended up hacking my own version of the same idea, but with an explicit reference to a mutable data slot, much like the cursors in this post do.

I use a lot of Immutable.Record for domain objects, and I found that I could get nicely succinct code by adding a getter property to the cursor for each field in a record. It yields a new cursor if the value is an immutable collection too, or else the actual value the cursor points to. This seems like a weird heuristic until you realize that most programming languages make the same distinction (primitive values are cloned, object references are not).

This allows me to write code like

    <span>{this.props.user.group.name}</span>
Where this.props.user is a cursor, not an immutable collection. This is cool, because I only pass around subcursors around my component tree, but I can address them as if they're the values themselves when rendering. And I can still mutate them as a cursor.

I didn't publish this code, or even evaluate the performance of it, but if anyone cares I don't mind sharing.


(Author here) I explored an API like that, the problem I ran into was that often you don't want to refine all the way to a primitive; e.g. What is the value of the whole record so I can validate it? Sometimes you want the record, sometimes you want the cursor.


I noticed that you probably always want the value if the value is a leaf of the tree (typically a primitive value). Similarly, for validation, do you validate records of the values on the records? If the syntax to access the values via a record cursor or via a record is exactly the same, then you can just pass the cursor to the validation function, rather than the record. The validation function doesn't even need to know that it got a cursor.

I'm curious whether you could shoot any holes in that reasoning.


I'd be interested... I'm setting up a new react project and just diving into which immutable data piece to use. Seems there's no real consensus at this point which is tricky, I'd love to see more examples.


I believe that's exactly what I was planning to do for my next project based on React. I'm very interested in your implementation.


Can someone give real world examples when this would be better? It seems to be copy-on-write in a way.


Assuming you are referring to immutability in general, this post does a great job of showing the benefits.

http://swannodette.github.io/2013/12/17/the-future-of-javasc...


This sounds snarky, but I mean it earnestly. Rather than move from JS to ClojureScript one library at a time, why not step all at once? I think by the time you use a bunch of libraries this way, you will be almost as far from common JS practice as you would just using CLJS in the first place.


I could think of a few reasons why not. Learning curve being a big one. For startups it's the difference in finding developers. Preference could be another, whether you think imperative code to be more readable, or maybe you enjoy classes or other JS-targeting languages.

I've looked into Om/ClojureScript now a few times and the learning curve is steep and keeps putting me off, but also I fear that if I start a project with it I'll alienate potential future devs and have difficulty gaining traction. Plus, with ES6/CoffeeScript/TypeScript and libraries like this it's more possible than ever to build a beautiful environment to code in that doesn't require such a big jump.

But hey, at least this stuff brings more exposure to ClojureScript. That's definitely a good sign.


Clojurescript might be more of a magnet than a filter in finding developers. I think any developer would be excited to learn the language that influenced React and the immutable js libraries... as long as they had some guidance and employment to do it!

In my experience, I joined a company specifically to learn/use clojurescript and others joined the same time, and we were committing code after 2 weeks.

Om has a steep learning curve. I'd recommend quiescent if you want the bare minimum for interfacing with React in cljs.


There is a segment of the JavaScript/Node community that embraces the UNIX small components that do one thing well philosophy. We see all of these things as incremental improvements to be used or not depending on need. Using something like ClosureScript cuts you off from that.


I'm into clojure and clojurescript, but I can see why people would want to avoid the overhead of using a compile to js language. To be honest though, I'm excited about this and all other functional goodies coming to plain JS from om and react (immutable data structure libraries, virtual-dom) because it'll be easier to use them from purescript.


If your company is already using JavaScript and React, it's going to be a whole lot easier to get them to let you use React-cursor than it would be to get them to move over to ClojureScript and Om.


Perhaps when ClojureScript is self hosted. Right now the compile time, project setup, and overall learning curve is a pretty big barrier.


I love Clojure but I do not like Clojure's slow build times.


ClojureScript auto-compiler is really fast (under a second) after the first compile if you don't use advanced optimizations.


ClojureScript auto-compiler doesn't run in my web browser, where I want compilation to happen. Not a shell script I have to manually start every time I want to develop.


I'm confused. Is there a tutorial or book that explains all the (modern) Javascript software design concepts mentioned in this readme?


If its the functional programming jargon you're interested in, you might start with Javascript Allonge [1]

[1] https://leanpub.com/javascript-allonge/read


I highly recommend this book as well, although it would probably be of limited help with the concepts in this project. I'd suggest reading more about React, the Om project, and immutable data structures to get some background.



As someone who has been building on a react gui for some time I can see how this could take away some of the worst boilerplate that remains - in the form of callbacks calling callbacks. Exiting! But it seems it doesn't go all the way. I imagine I still need to do some boilerplating. I don't directly see how that would be avoided though.


In Om, you can't create Cursors to primitive values. Rather, you can only create Cursors to indexable collections, like a Map or Vector. Looks like React-Cursor allows this, so I'll take another look to see what's preventing this in Om.


You can create cursor on primitives in Om, it's just a can of worms as it makes clear the dichotomy between native value & Object versions of those values.



I went for a minimalist implementation that leverages the work done in React's Immutability Helpers (http://facebook.github.io/react/docs/update.html). The API allows you to use the same `this.state.xxx` you're used to which I find nice.


How do I use the cursor to build a recursive json editor that allows appending the tree? Does onChange support adding new elements to the cursor or can I only operate on elements that have been previously fed to the cursor?


if your state is { foo: ['a','b','c'] }, you can either do:

    cursor.refine('foo', 3).onChange('d')
    cursor.refine('foo').onChange(['a', 'b', 'c', 'd'])
(indexes in a js array are the same as keys in a map)

It is possible to implement `push` and other list/object ops that are exposed by React.addons.update, they have not been implemented yet because they make the interface more complicated.

http://facebook.github.io/react/docs/update.html

I opened an issue for discussion: https://github.com/dustingetz/react-cursor/issues/10


Thanks, I was more wondering whether I can have something like :

  cursor.refine('foo').onChange({'child':{}});
in one place and then in some other, code that would dwelve inside that child recursively (and append the "add child" action to that node as well)

My goal is to have a json editor with json as the only model in the application and have react/cursor automatically render the tree, and allow its modification (removing and adding nodes).

Such functionality would require cursor to "dive" inside a json node without knowing its name - I'm not sure whether the 'push' op you are mentioning would be required to achieve this.


This might be of topic, but is there a clojure edn visualiser/editor that works like that json editor?


Give ankha a try. It's still in early development: https://github.com/noprompt/ankha


Man I really need to learn React.js, this stuff is just getting more and more interesting by the day.


This project template can be useful when playing with React.js

https://github.com/kriasoft/react-starter-kit


My guess is this can get really slick when we have ES6 Proxy capabilities.


Am being I slow? I can't see the source code to the JSON editor.



iirc doesn't Reagent do this automatically via caching?




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: