alexgalays / immupdate Goto Github PK
View Code? Open in Web Editor NEWimmutable updates for JS Objects and Arrays
immutable updates for JS Objects and Arrays
I want to use this in a React Native project but its packager tries to compile everything in node_modules, which snags on the .babelrc in this package directory because the project doesn't have the same babel presets installed. Ignoring .babelrc (and the other build files for good measure) and pushing a new version should fix it.
h1 = {
"tag": "Group",
"props": {
"styles": {
"display": "flex",
"flex-direction": "row"
}
},
"children": [
{
"props": {
"class": "employee-name",
"styles": {
"color": "#33cc33"
},
"value": "Jane Doe "
},
"tag": "Text"
},
{
"props": {
"class": "employee-photo",
"url": "http..."
},
"tag": "Image"
}
]
}
// Does not work with 0
let h11 = deepUpdate(h1).at('children').at(0).at("props").at("styles").withDefault({}).at('background-color').set('#ffeedd')
// Does work with 1
let h11 = deepUpdate(h1).at('children').at(1).at("props").at("styles").withDefault({}).at('background-color').set('#ffeedd')
My full code atached
I just found out that the latest versions of immupdate don't allow what I call "updater functions" to be passed in the spec object. I find this most regrettable, as I had come to depend on this feature, doing things like:
import {add} from 'ramda'
update(state, { counter: add(1) })
It gave me a super clean and expressive API and I'm absolutely not willing to let go of this style. I understand that you must have your own reasons for this design decision, but I wanted to give you sincere feedback, and for me this change is a deal breaker.
Anyway, I'd be happy to discuss it further if you wish to.
For this code:
export function x<STATE>(form: React.Component<{}, STATE>): ChangeHandler<STATE> {
return (keys: keyof STATE) => (newValue: any) =>
form.setState<keyof STATE>(state => deepUpdate(state).at(keys).set(newValue))
}
This function compiled with the version 1.2.2
of immupdate, but not with version 1.3.1
. I get the following error:
ERROR in /Users/guillaumebersac/projects/ciam-app/frontend/console/common/component/form/changeHandler.ts
./common/component/form/changeHandler.ts
[tsl] ERROR in /Users/guillaumebersac/projects/ciam-app/frontend/console/common/component/form/changeHandler.ts(35,59)
TS2339: Property 'at' does not exist on type 'Updater<Readonly<STATE>, Readonly<STATE>>'.
It looks like typescript does not infer that the return value of deepUpdate
is an ObjectUpdater
in this case and not just a Updater
.
It looks like the compiler understand the returned object of deepUpdate is an ObjectUpdater, but doesn't take it into account.
Hi there, thanks for the awesome library. I'm not sure if this is a bug or not however i've observed the following when trying to deep update a map using withDefault
, which I'd like to use to set default values for an object, just once.
interface Panel {
id?: string;
isCollapsed?: false;
}
const DefaultPanel: Panel = { isCollapsed: false};
interface PanelCollection {
panels: {[panelId: string]: Panel}
}
const initial: PanelCollection = {
panels: {}
};
const updatedPanel = {id: 'xxxx-xxxxx' };
const updated = deepUpdate(initial).at('panels').at(updatedPanel.id).withDefault(DefaultPanel).modify(p => { return {...p, ...updatedPanel} });
Here is the stacktrace
immupdate.js:96 Uncaught TypeError: Cannot read property 'abort' of undefined
at Updater.cloneForUpdate (immupdate.js:96)
at doModify (immupdate.js:53)
at Updater.modify (immupdate.js:60)
at Object.527 (reducers.ts:48)
at webpack_require (bootstrap f93ece5…:52)
at Object.572 (app.module.ts:78)
at webpack_require (bootstrap f93ece5…:52)
at Object.571 (app.component.ts:104)
at webpack_require (bootstrap f93ece5…:52)
at Object.518 (src async:110)
at webpack_require (bootstrap f93ece5…:52)
at Object.1099 (actions.ts:86)
at webpack_require (bootstrap f93ece5…:52)
at webpackJsonpCallback (bootstrap f93ece5…:23)
at main.bundle.js:1
This however does seem to work, though just less efficiently
deepUpdate(initial).at('panels').at(updatedPanel.id).modify(p => { return {...DefaultPanel, ...p, ...updatedPanel} });
Does this look like an easy fix ?
Hi,
Would you please add license information to your library. We are using https://github.com/prometheusresearch/react-forms which has dependency from immupdate which is distributed under MIT license. But license information in immupdate is missing. I'm pretty sure that adding "license": "MIT" to package.json should be enough.
Thanks in advance.
Nice library! I fully agree with what you wrote in the README about "Why Object/Array instead of immutable data structures". I'd love to use it everywhere instead of deeply nested object splice expressions.
What keeps me from completely switching to deepUpdate though is that it does not deal well with union types. For example this:
import { deepUpdate } from 'immupdate'
interface A {
name: 'a',
foo: number,
}
interface B {
name: 'b',
bar: string,
}
interface Container {
aOrB: A | B
}
const c: Container = {aOrB: {name: 'a', foo: 1}}
deepUpdate(c).at('aOrB').at('foo').set(2)
results in the following type error for me (using lots of strict flags in tsconfig and Typescript 2.6):
The 'this' context of type 'BoundUpdater<Container, A | B>' is not assignable to method's 'this' of type 'BoundAtUpdater<Container, any[]>'.
Types of property '__T' are incompatible.
Type '[Container, A | B]' is not assignable to type '[Container, any[]]'.
Type 'A | B' is not assignable to type 'any[]'.
Type 'A' is not assignable to type 'any[]'.
Property 'includes' is missing in type 'A'.
I mean, raising a compilation error here totally makes sense because there are no typeguards anywhere around .at('foo')
.
Do you have any thoughts about how one could solve this without giving up too much of the deepUpdate convenience? Is it even possible to use typeguards and generics together in TS?
Came across this problem while updating to the latest version of space-lift today. Great work on that, by the way!
import { deepUpdate } from "immupdate"
function handleChange<T extends object>(values: T, value: T[keyof T], name: keyof T) {
return deepUpdate(values)
.at(name)
.set(value);
};
Test it yourself by downloading this example gist and running npm install && npm run build
It compiles, like it did with version 1.1.7
example.ts:4:12 - error TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((index: number) => BoundUpdater<T, [T] extends [any[]]
? T[number & keyof T] | undefined : never>) | (<K extends keyof T>(key: K) => BoundUpdater<T, T[K]>)' has no compatible call signatures.
4 return deepUpdate(values)
~~~~~~~~~~~~~~~~~~
5 .at(name)
~~~~~~~~~~~~~~~~~
Tested with TypeScript versions 2.8.4, 2.9.2 and 3.0.1, with the same results.
I don't know if this is a bug or if I am not using this library correctly.
If I run this code :
deepUpdate(profile)
.at('addresses')
.abortIfUndef()
.at(2)
.set(immupdate.DELETE)
On this object :
{
"addresses": [
{
"country": "France",
"default": true,
"locality": "paris",
"street_address": "3 avenue du Roi",
"title": "principale"
},
{
"country": "France",
"locality": "lille",
"postal_code": "12345",
"street_address": "blabla"
},
{
"country": "France",
"locality": "Niors",
"postal_code": "85234",
"street_address": "blabla"
}
]
}
I get this result (the problem is the trailling null
at the end of the array) :
{
"addresses": [
{
"country": "France",
"default": true,
"locality": "paris",
"street_address": "3 avenue du Roi",
"title": "principale"
},
{
"country": "France",
"locality": "lille",
"postal_code": "12345",
"street_address": "blabla"
},
null
]
}
Instead of this (which is the result I try to get) :
{
"addresses": [
{
"country": "France",
"default": true,
"locality": "paris",
"street_address": "3 avenue du Roi",
"title": "principale"
},
{
"country": "France",
"locality": "lille",
"postal_code": "12345",
"street_address": "blabla"
}
]
}
How do we delete an element from an array with immupdate?
Try the sample here : https://repl.it/repls/AdequateDirectVirtualmachines
Maybe an external helper could be bundled with this library, but imported separatedly:
const state = require('immupdate/state')({initial: 76})
state.get() // {initial: 76}
state.update({fruit: 'banana', initial: 84})
state.get('fruit') // 'banana'
state.get() // {initial: 84, fruit: 'banana'}
I've done something like that and was thinking about publishing it as a standalone package depending on immupdate
, but perhaps it is better to bundle it here, somewhat like h
in bundled into virtual-dom
, if more people find this pattern useful.
While using deepUpdate I found out the hard way that the value provided as default was modified in place. I would guess it is not intended behaviour.
with immudate v 1.2.0 (imported via space-lift
), I check and this is also the case with 1.2.3.
Here is a small script to reproduce :
const { deepUpdate } = require('immupdate')
const valueToUpdate = { aField: ''}
const defaultValue = { foo: 'aDefaultValue' }
deepUpdate(valueToUpdate)
.at('otherField')
.withDefault(defaultValue)
.at('foo')
.set('valueChanged!')
// defaultValue.foo is now 'valueChanged!'
console.log(JSON.stringify(defaultValue))
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.