npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

reupdate

v1.12.0

Published

Selectors & React.PureComponent friendly immutable update

Downloads

43

Readme

Selectors & React.PureComponent friendly immutable update

reupdate can help you to:

  • Reduce recalculations in existing reselect selectors used redux state: just create reducers with reupdate
  • Even more reduce recalculations with reupdate selectors (with same API as reselect)
  • Reduce re-rendering of your react components

Examples

Note

reupdate has no dependencies to redux and react, etc. so you can use it with other frameworks.

Contents

Motivation

Typically, the state changes less frequently than it is read.

To avoid extra re-evaluations (and re-rendering) with redux, reselect, react and others we should return same reference to the (maybe nested) state when it was updated but actually not changed (some isDeepEqual(state, nextState) gives true).

Design rule

If your update of src value do not change it (in sense of deep equality) then result === src must give true:

isDeepEqual(src, value) |=> result === src

This rule also must work for nested not changed values as is:

isDeepEqual(src.a.b.c, value.a.b.c) |=> result.a.b.c === src.a.b.c

Difference from object-path-immutable and immutability-helper

object-path-immutable and immutability-helper usually expect that you know what is the difference from src and value and some times returns reference for value despite it is deep equal to src. As a result we have extra recalculations of selectors and/or re-rendering of components.

In such cases reupdate returns reference to src, so it prevents extra recalculations and re-rendering. Profit! See examples!

Install

yarn add reupdate

or

npm i reupdate

Quick usage

import {set, setAt, extend} from 'reupdate';
import cloneDeep from 'lodash/cloneDeep';

const src = {
  a: {
    b: {
      c: 'c',
      d: 'd'
    },
    e: 'e',
    f: [
      {f0: 'f0'},
      {f1: 'f1'},
      {f2: 'f2'}
    ]
  },
  info: {
    name: 'smth',
    coord: {x: 1, y: 2}
  }
}

let res;
res = set(src, src); // res === src
res = set(src, cloneDeep(src)); // res === src

res = setAt(src, 'a.f[1].f1', 123);
// res.a !== src.a
// res.a.f !== src.a.f
// res.a.f[1].f1 !== src.a.f[1].f1
/** But following references were saved!: */
// res.a.b === src.a.b
// res.a.e === src.a.e
// res.a.f[0] === src.a.f[0]
// res.a.f[2] === src.a.f[2]

res = extend(src, { 
  info: {
    name: 'New Name', 
    coord: {x: 1, y: 2} // coord not actually changed 
  }
});
// res.info !== src.info
// res.info.name !== src.info.name
/** But following references were saved!: */
// res.a === src.a
// res.info.coord === src.info.coord 

Imports

You can import functions like this:

import {set, insert, createSelector} from 'reupdate';

Or like this (to reduce bundle size with webpack or other bundlers):

import set from 'reupdate/set';
import insert from 'reupdate/insert';
import createSelector from 'reupdate/createSelector';

API

set(value, newValue)

Returns value if newValue has nothing new (is deep equal to value).

Returns new object/array with:

  • Same references to not changed properties/items
  • New references to changed properties/items

Example:

import set from 'reupdate/set'

const src = {
  name: 'Alex',
  info: {
    greeting: 'Hello',
    description: 'I am developer'
  },
  address: {
    country: 'Russia',
    city: 'Moscow'
  },
  friends: [
    { name: 'Vasya', age: 25 }, 
    { name: 'Fedor', age: 33 }
  ]
};
const replacement = {
  name: 'Alex',
  info: {
    greeting: 'Hello',
    description: 'I am developer'
  },
  address: {
    country: 'Russia',
    city: 'Moscow'
  },
  friends: [
    { name: 'Vasya', age: 3 }, // The ONLY actual change!  
    { name: 'Fedor', age: 33 }
  ]
};

const res = set(src, replacement);

expect(res).toEqual(replacement); // Correct result

expect(res === src).toBe(false);
expect(res.info === src.info).toBe(true); // Same reference!
expect(res.address === src.address).toBe(true); // Same reference!
expect(res.friends === src.friends).toBe(false);
expect(res.friends[0] === src.friends[0]).toBe(false);
expect(res.friends[1] === src.friends[1]).toBe(true); // Same reference!

setAt(value, path, newValue)

set nested part of value

updateAt(value, path, updater)

Example:

updateAt(state, 'a.b[1].c', c => c + 1)

deleteAt(value, path)

Equal to setAt(value, path, undefined)

Important edge case: delete undefined value saves reference:

import deleteAt from 'reupdate/deleteAt';

const value = {x: 1};
const res = deleteAt(value, 'y');
// value === res

extend(src, extensionOrCreator)

Params:

  • src: source Object
  • extensionOrCreator: Object | Object => Object
    • Extension object
    • Function for creating extension object from src

Returns src if extensionOrCreator has nothing new (has deep equal only properties).

Returns new object with:

  • Same references to not changed properties
  • New references to changed properties

Important edge case: extend with empty extensionOrCreator saves reference: src === extend(src, {})

import extend from 'reupdate/extend';

const src = {
  name: 'Alex',
  info: {
    greeting: 'Hello',
    description: 'I am developer'
  },
  address: {
    country: 'Russia',
    city: 'Moscow'
  },
  friends: [
    { name: 'Vasya', age: 25 }, 
    { name: 'Fedor', age: 33 }
  ]
};
const extension = {
  friends: [
    { name: 'Vasya', age: 3 }, // The ONLY actual change! 
    { name: 'Fedor', age: 33 }
  ]
};

const res = extend(src, extension);

expect(res).toEqual({...src, ...extension}); // Correct result

expect(res === src).toBe(false);
expect(res.info === src.info).toBe(true); // Same reference!
expect(res.address === src.address).toBe(true); // Same reference! 
expect(res.friends === src.friends).toBe(false);
expect(res.friends[0] === src.friends[0]).toBe(false);
expect(res.friends[1] === src.friends[1]).toBe(true); // Same reference!

extendAt(src, pathToObject, extensionOrCreator)

extend nested object of value

Params:

  • pathToObject: string - lodash path string
  • See extend

push(srcArray, ...values)

Important edge case: push empty values saves reference: srcArray === push(srcArray)

pushAt(src, pathToArray, ...values)

Example:

const state = {a: {b: [{x: 1}, {y: 2}]}}
const newState = pushAt(state, 'a.b', {z:3}, {w: 4})
// newState = {a: {b: [{x: 1}, {y: 2}, {z:3}, {w: 4}]}}

Important edge case: pushAt empty values saves reference: src === pushAt(src, 'a.b')

pop(srcArray, n = 1)

Important edge case: pop 0 items saves reference: srcArray === pop(srcArray, 0)

popAt(src, pathToArray, n = 1)

Important edge case: pop 0 items saves reference: src === popAt(src, 'a.b', 0)

insert(srcArray, atIndex, ...values)

Important edge case: insert empty values at any index saves reference: srcArray === insert(srcArray, i)

insertAt(src, pathToArray, atIndex, ...values)

Important edge case: insert empty values at any index saves reference: src === insert(src, 'a.b', i)

splice(srcArray, atIndex, deleteCount, ...values)

Important edge cases:

  • Delete 0 and insert empty values saves reference: srcArray === splice(srcArray, i, 0)
  • Delete and insert the same values saves reference:
    import splice from 'reupdate/splice';
    const srcArray = [a, b, c, d];
    const atIndex = 1;
    const res = splice(srcArray, atIndex, 2, b, c);
    // srcArray === res;

spliceAt(src, pathToArray, atIndex, deleteCount, ...values)

Important edge cases:

  • Delete 0 and insert empty values saves reference: src === spliceAt(src, 'a.b', i, 0)
  • Delete and insert the same values saves reference (see splice example)

shift(srcArray, n = 1)

Important edge case: shift 0 items saves reference: srcArray === shift(srcArray, 0)

shiftAt(src, pathToArray, n = 1)

Important edge case: shift 0 items saves reference: src === shift(src, 'a.b', 0)

unshift(srcArray, ...values)

Important edge case: unshift empty values saves reference: srcArray === unshift(srcArray)

unshiftAt(src, pathToArray, ...values)

Example:

const state = {a: {b: [{x: 1}, {y: 2}]}}
const newState = pushAt(state, 'a.b', {z:3}, {w: 4})
// newState = {a: {b: [{z:3}, {w: 4}, {x: 1}, {y: 2}]}}

Important edge case: unshiftAt empty values saves reference: src === unshiftAt(src, 'a.b')

createSelector(...inputSelectors | [inputSelectors], resultFunc)

Wrapper for reselect.createSelector

createStructuredSelector({inputSelectors}, selectorCreator = createSelector)

Wrapper for reselect.createStructuredSelector

Tests

Run yarn test index to check that reupdate saves references.

Run yarn test object-path-immutable to check that object-path-immutable NOT saves references (some tests will fail).

Run yarn test immutability-helper to check that immutability-helper NOT saves references (some tests will fail).

Credits