Immutable state for React.js

Overview

react-cursor

Immutable state for React.js

react-cursor hello-world in a fiddle

live demo

What is react-cursor

Cursors are a tool for working with recursive or deeply nested data, immutably. react-cursor is a javascript port of an abstraction that I first saw in ClojureScript. This implementation is decoupled from any rendering library and is very small.

Cursors are useful in UI programming, because UIs are tree shaped and naturally have tree-shaped state. Cursors let your app hold all its state in one place at the root of the UI tree; thus the root is stateful, and all downtree views are stateless.

Project Maturity

master is stable, there is a full test suite.

API

Cursor interface has three methods: value, swap and refine.

  • cur.value() return the value in the cursor at some path.
  • cur.refine(path, ...paths) return a cursor nested inside another cursor
  • cur.swap(f) apply f to the value in the cursor value and puts returned value into the backing store

For frequently used swap functions, see the bundled update-in dependency: see here. Cursor instances have optional syntax sugar for the swap fns provided by update-in; see CursorOperations.js

FAQ

  • Cursors have value semantics, don't mutate values that come out of a cursor
  • Equal cursors are === for easy and efficient optimized rendering (see hello world jsfiddle for example)
  • You should read the source! The core cursor abstraction is 15 lines of code
  • There is an undocumented alternate implementation, RefCursor, which has reference semantics, this is only useful for working with legacy mutable code

License

react-cursor is governed under the MIT License.

Attributions

react-cursor was built by Daniel Miladinov and Dustin Getz.

Comments
  • Bug related to cacheing/memoizing of old component states

    Bug related to cacheing/memoizing of old component states

    Hi, I'm encountering some trouble with react-cursor. The bug that I'm encountering is in a relatively simple component where I add/remove items from a list. The bug manifests itself in many ways. The simplest is that if I add an item to the list and the try to remove it, the item will not be removed.

    When I attempted to debug this by looking into what react-cursor is doing, it appears that the memoize function calculates the hashed key for the new state, in which there are no items in the list, and then checks to see if the hashed key is already in the cache variable. Strangely, the hashed key for the component state with items in the list is the same as the new state without the list, and so the memoize returns the previously cached component that still has the item in the list.

    I've been trying to debug this myself, since I'd like to contribute to your project, but have hit a bit of a wall. From what I've found it seems as though there could be a problem in the hash function such that it returns the same value for two different strings. Any suggestions?

    opened by ezmiller 25
  • As of React 13, can Cursor be decoupled from React components?

    As of React 13, can Cursor be decoupled from React components?

    One real world issue we have run into, is that when React state is conceptually at the top, but actually it isn't at the top because there's non-react portions of the system above it, engines above it etc

    As of React 13, cursor doesn't need to use a react private api anymore, because of the improved setState api. So Cursors really only depend on two things conceptually: a setState function, and a current state value. It doesn't actually have to be a React component anymore.

    So given this, I believe I could implement the setState/value api outside of react and backed by regular javascript objects, thus cursors can be constructed above the React entry point. This is analogous to the model provided by other cursor implementations (ImmutableJS and Baobab), except it keeps the benefits of react-cursor over those approaches - namely, simplicity of code (a lot fewer lines of code), and simplicity of types (no special immutable types, just regular javascript objects)

    Could be worth exploring.

    opened by dustingetz 21
  • optimizing shouldComponentUpdate

    optimizing shouldComponentUpdate

    Since shouldComponentUpdate is hot code, I figured it'd be worth optimising it by paying some readability. This PR therefore...

    • simplifies refsChanged checks if we just have a single ref field
    • simplifies valuesChanged if we aren't ignoring any ref fields
    • preruns the ignore union
    • minimizes object lookups
    • only calculates refsChanged if valuesChanged wasn't true
    • minimizes function calls
    opened by krawaller 14
  • Cursor exception in deepFreeze() on IE

    Cursor exception in deepFreeze() on IE

    Object.isFrozen throws an exception when called with 'null'. Use an isObject() function that tests for null.

    Also, ignore functions because they can have recursive members (e.g. constructors).

    opened by newyankeecodeshop 12
  • Enjoy separated addons - react-addons-update

    Enjoy separated addons - react-addons-update

    opened by lijunle 11
  • API to remove me from parent

    API to remove me from parent

    During my plays with react-cursor I've often wished I could delete a cursor, removing it from its parent (if it has one). Or am I misunderstanding something in barking up this tree? Say for example we wanted to add a delete button to the Clicker components in the helloworld webapp example. Is there a better way to do that without my imaginary delete API?

    opened by krawaller 11
  • Can we detect and warn on out-of-band mutations?

    Can we detect and warn on out-of-band mutations?

    Prevents #47. I'm not entirely convinced that it's possible to detect out-of-band mutations. I haven't thought it all the way through.

    One idea, is that the cursor update methods (set, push, etc.) can hang on to a reference to the final value right before they pass it to react setState. Then in each subsequent invocation to a cursor update method, we can recursively check that the current cursor.value is reference-equal to the previous value at all nodes of the tree, before performing the update. This would have performance implications.

    opened by dustingetz 10
  • Removed underscore dependency and preparation for renaming.

    Removed underscore dependency and preparation for renaming.

    I removed underscore as an dependency by adding small dependencies (without further big dependencies just the necessary functionality: union/equal/omit) or own implementations. In most places the ES5 functionality is used and polyfilled if not available by Mozilla's polyfills (see polyfill.js). See commit 3b33b74.

    onChange is still available for backwards compatibility, but if Cursor.debug is set to true warnings will be emitted, that should help tracking down uses for a flawless transition. The new method set is available providing the onChange functionality without the warnings (see 6f8e569). This commit also contains preparation for additional functionality like push/merge updates.

    At last I rebuild both the example and dist and increased the version number to 0.9.1 (see 0bd60f2), which means if the pull request is excepted a new version needs to be pushed to npm.

    Relevant issues: #10 #9 #6

    opened by trevex 10
  • Use of the word

    Use of the word "orthoganal" in docs

    As with programming, it is usually a good idea in language to use the simplest possible solution. Is there a way to communicate what is meant by orthogonal with a more easily understood word? Do you mean that react-cursor and Flux represents opposite approaches?

    opened by ezmiller 8
  • Support map(), filter(), find() and forEach() on cursors

    Support map(), filter(), find() and forEach() on cursors

    One of the things I miss from Cortex is the ability to use map(), filter(), find(), findIndex(), and forEach() on cursors over lists and keys(), values(), and forEach() on cursors over hashes. These should work like their native equivalents but their return values should be cursors.

    For example, I want to pass the currently selected item in a list of hashes down to a child component. The currently selected item is identified by having an attribute selected set to true:

    var selected = this.props.someList.find(a => a.selected.val());
    

    With react-cursor, I (think I) have to write:

    var _ = require('lodash');
    
    var selectedIndex = _.findIndex(this.props.someList.value, a => a.selected);
    var selected = this.props.someList.refine(selectedIndex)
    

    We seem to have operations to mutate the cursor, but only value for accessing it.

    opened by optilude 8
  • Implement merge, push, apply, etc

    Implement merge, push, apply, etc

    React.addons.update supports more than just $set, we could easily expose these functions. http://facebook.github.io/react/docs/update.html

    It would increase the API surface area from four methods to nine.

    Say something here if you have an opinion.

    opened by dustingetz 8
  • can a refined cursor call forceUpdate on the proper react node to bypass trickle down rendering?

    can a refined cursor call forceUpdate on the proper react node to bypass trickle down rendering?

    A lot of people prefer component-local state over cursors because of the intuitive performance cost of trickle down rendering.

    How might we cause a derived cursor to forceUpdate it's react node, causing a render directly instead of trickle-down rendering of nodes in the path from root to node? Is this possible?

    opened by dustingetz 2
  • Implement default values at a subtree

    Implement default values at a subtree

    The problem is that, React state has getInitialState, so the default state can be defined inside the component. With cursors, the default state has to be declared somewhere else, at the top.

    So suppose refine could take an optional extra argument, a fallback value

    state = {a: {b: null }}

    cursor.refine(['a', 'b']).value //=> null var not_found = {c: {d: 42}} c2 = cursor.refine(['a', 'b'], not_found); c3 = c2.refine(['c', 'd']); c3.value //> 42 c3.set(c3.value + 1); //> {a: {b: {c: {d: 43}}}}

    I did implemented this in clojurescript and I found this helped clean up my components quite a bit.

    opened by dustingetz 4
Owner
Dustin Getz
Founder, principal architect @hyperfiddle http://www.hyperfiddle.net
Dustin Getz
The simple but very powerful and incredibly fast state management for React that is based on hooks

Hookstate The simple but very powerful and incredibly fast state management for React that is based on hooks. Why? • Docs / Samples • Demo application

Andrey 1.5k Jan 7, 2023
A tiny state manager for React, Svelte, Vue and vanilla JS

dotto.x Dotto.x is a tiny state manager for React, Svelte, and vanilla JS. Lightweight. Core is less than 135 bytes (minified and gzipped). Zero depen

null 108 Nov 2, 2022
Teaful - Tiny, easy and powerful React state management

Tiny, easy and powerful React state management library What advantages does it have? ✨ ?? ・Tiny: Less than 1kb package to manage your state in

Teaful 668 Dec 18, 2022
🔍 Holmes is a 0 config, fast and elementary state orchestrator for React

React Holmes ?? - Elementary State Orchestrator for React ?? Holmes is a 0 config, fast and elementary state orchestrator for React. Holmes has a very

null 49 Oct 15, 2022
An event-based global state management tool for vue, react, mini-program, ect.

hy-event-store An event-based global state management tool for vue, react, mini-program, etc. 一个基于事件的全局状态管理工具,可以在Vue、React、小程序等任何地方使用。 设计灵感 在项目中找到一个更加

null 260 Jan 2, 2023
micro reactive state management - just for fun

effectus micro reactive state management - just for fun Install $ yarn add effectus Usage import { effect, signal } from 'effectus' const [name, set

Hector Zarco 1 Dec 7, 2021
A tiny, reactive JavaScript library for structured state and tabular data.

A JavaScript library for structured state. Using plain old JavaScript objects to manage data gets old very quickly. It's error-prone, tricky to track

tinyplex 1.4k Dec 30, 2022
React, React Native and Vue UI components for building data-driven apps with Elasticsearch

Reactive Search UI components library for Elasticsearch: Available for React, Vue and React Native Read how to build an e-commerce search UI a.) with

appbase.io 4.7k Jan 4, 2023
⚡️ Lightning-fast search for React and React Native applications, by Algolia.

React InstantSearch is a library for building blazing fast search-as-you-type search UIs with Algolia. React InstantSearch is a React library that let

Algolia 2k Dec 27, 2022
React ESI: Blazing-fast Server-Side Rendering for React and Next.js

React ESI: Blazing-fast Server-Side Rendering for React and Next.js React ESI is a super powerful cache library for vanilla React and Next.js applicat

Kévin Dunglas 633 Jan 5, 2023
react-pdf-highlighter is a React library that provides annotation experience for PDF documents on web.

☕️ Buy me a coffee react-pdf-highlighter react-pdf-highlighter is a React library that provides annotation experience for PDF documents on web. It is

Vladyslav 9 Dec 2, 2022
React-cursor-chat - A react component for cursor chat

@yomo/react-cursor-chat ?? Introduction A react component for cursor chat Press

YoMo 84 Nov 17, 2022
A minimum and lightweight external store for React and React Native application

Reactive store - a minimum and lightweight external store for React and React Native application Table of contents Features Installation Usage 3.1 Cre

Hòa Võ 7 Nov 21, 2022
Automatically AJAXify plain HTML with the power of React. It's magic!

React-Magic and HTMLtoJSX React-Magic is an experimental library that uses the power of Facebook's React library to inject AJAX-loading goodness into

React Community 921 Dec 16, 2022
A set of React components implementing Google's Material Design specification with the power of CSS Modules

React Toolbox is a set of React components that implement Google's Material Design specification. It's powered by CSS Modules and harmoniously integra

React Toolbox 8.7k Dec 30, 2022
CSS media queries in react - for responsive design, and more.

react-responsive Information Package react-responsive Description Media queries in react for responsive design Browser Version >= IE6* Demo The best s

contra 6.5k Dec 30, 2022
Mobile App Framework powered by React

TouchstoneJS Mobile App Framework powered by React. See the demo at touchstonejs.io. Follow @touchstonejs on Twitter for updates. See the touchstone-s

TouchstoneJS 3.3k Jan 5, 2023
Maple.js is a React webcomponents based framework mixing ES6 with Custom Elements, HTML Imports and Shadow DOM. It has in-built support for SASS and JSX, including a Gulp task for vulcanizing your project.

Heroku: http://maple-app.herokuapp.com/ npm: npm install maple.js Bower: bower install maple.js Maple is a seamless module that allows you to organise

Adam Timberlake 430 Dec 23, 2022
A performant, scalable and pluggable approach to instrumenting your React application.

react-i13n react-i13n provides a performant, scalable and pluggable approach to instrumenting your React application. Typically, you have to manually

Yahoo 369 Dec 25, 2022