Code Monkey home page Code Monkey logo

proxyequal's Introduction

proxyequal

CircleCI status Greenkeeper badge

Shallow equal is a good thing, but it compares thing you don't need.

Proxy equal - "MobX"-like solution, which will "magically" compare only used keys.

NPM

Usage

  • Wrap an object with proxyState

  • Run some computations using providing proxyObject. proxyState returns object with shape

    • state - a double of provided state, with tracking enabled
    • affected - list of used keys in a state.
    • seal - disables tracking
    • unseal - enabled tracking
    • replaceState(newState) - replaces top level state, maintaining rest of data.
    • reset - resets tracked keys
  • proxy will collect all referenced or used keys

  • proxyEqual will compare all used "endpoint" keys of 2 objects

  • proxyShallow will compare all used NON "endpoint" keys of 2 objects.

The difference between proxyEqual and proxyShallow is in expectations.

  • proxyShallow is similar to shallowEqual, it compares the top level objects. Might be they are still the same.
  • proxyEqual working on variable-value level, performing (very) deep comparison of objects.

Extra API

  • spreadGuardsEnabled(boolean=[true]) - controls spread guards, or all-keys-enumeration, which makes proxyEqual ineffective.
  • sourceMutationsEnabled(boolean=[false]) - controls set behavior. By default proxied state is frozen.

When to use proxyequal

When you have a big state, for example redux state, but some function (redux selector, or mapStateToProps) uses just a small subset.

Here proxyequal can shine, detecting the only used branches, and comparing only the used keys.

Example

import {proxyState, proxyEqual, proxyShallow} from 'proxyequal';

// wrap the original state
const trapped = proxyState(state);

// use provided one in computations
mapStateToProps(trapped.state);

// first shallow compare
proxyShallow(state, newState, trapped.affected);

// next - deep compare
proxyEqual(state, newState, trapped.affected);

Don't forget to disable

const trapped = proxyState(state);
// do something
workWith(trapped.state);

trapped.seal();

// now tracking is disabled

trapped.unseal();
// and enabled

Speed

Uses ES6 Proxy underneath to detect used branches(as MobX), and search-trie to filter out keys for shallow or equal compare.

So - it is lighting fast.

Limitations

Unfortunately, due to Proxy wrappers all objects will be unique each run.

 shallowEqual(proxyState(A), proxyState(A)) === false

There is a undocumented way to solve it, used internally in memoize-state library. Once it will be proven to work stable - we will expose it.

Compatibility

Requires Proxy support, so the proxy-polyfill is included in the common bundle for Internet Explorer 11. How this works may change in future, see issue #15 "ProxyPolyfill is unconditionally imported" for details.

Licence

MIT

proxyequal's People

Contributors

bz2 avatar dai-shi avatar greenkeeper[bot] avatar pie6k avatar thekashey avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

proxyequal's Issues

TypeError: 'get' on proxy: property 'byId' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected '#<Object>' but got '[object Object]')

Hey, I'm experimenting using memoize-state as an alternative to reselect on some selectors and I'm getting this error:

TypeError: 'get' on proxy: property 'byId' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected '#<Object>' but got '[object Object]')

According to the stack trace, the error is coming from the get method of the proxyequal proxy.

Here's a small reproducible without using any libraries (from this stackoverflow answer):

var obj = {};
Object.defineProperty(obj, "prop", { 
  configurable: false,
  value: {},
});

var p = new Proxy(obj, {
  get(target, property, receiver) {
    return new Proxy(Reflect.get(target, property, receiver), {});
  },
});

var val = p.prop;

Some observations:

  • Tested on Chrome latest and Firefox Developer Edition
  • I believe this is related to using immer on my reducers, immer freezes the objects.
  • This is only happening on development mode (probably because immer don't freeze on production)
  • Might also be related to nested selectors (one memoized selector that calls another one inside).
  • May be unrelated but it seems this is only happening with some selectors, and they call at least one external function.

I found this pull request which contains a fix, maybe it would work here as well. Here's the code.

If I understood correctly proxyequal needs to handle this case.
@theKashey What do you think?

Is this an expected behavior of proxyMap

Hi, I'm trying to improve https://github.com/dai-shi/react-hooks-easy-redux with proxyMap,
but it doesn't behave as I expect. Do I misunderstand the usage?

$ node 
> const { proxyState } = require('proxyequal')
undefined
> const proxyMap = new WeakMap()
undefined
> const state = { a: 1, b: 2 }
undefined
> const trapped1 = proxyState(state, null, proxyMap)
undefined
> trapped1.state.a
1
> trapped1.affected
[ '.a' ]
> const trapped2 = proxyState(state, null, proxyMap)
undefined
> trapped2.state.b
2
> trapped2.affected
[]
> trapped1.affected
[ '.a', '.b' ]

I was expecting trapped2.affected to be ['.b'] and trapped1.affected to be kept ['.a'].

How to check wich affected keys did change?

First of,

Thanks for this lib! It's great for performance improvements.

However, I've got quite large objects to compare, and they're not equal, but they should, and I want to inspect which property did change.

Is it possible to use compare and get some debug reports eg. Not equal because .key.otherkey.something is not equal

Add support for primitives (TypeError: Attempted to add a non-object key to a WeakSet)

I'm very happy using proxyEqual in my project.

I've created a few react hooks based on it and the way it can optimize renders is crazy!

However, I sometimes use it in generic hooks, where I don't know up-front what type of value will be used for the state.

And if it'll happen that such value will be primitive (eg. string, null), ProxyEqual will throw an error

TypeError: Attempted to add a non-object key to a WeakSet

add
    [native code]:0
proxyfy
    proxyEqual.js:133:21
proxyState
    proxyEqual.js:555:30
...

I've mitigated this issue by always wrapping my value inside a small object like { __value: valueHere }, but it's probably not the optimal way to do it as it's always adding one level of nesting when comparing anything.

I've tried to dynamically detect if I need such wrapper and wrap it or no when creating a proxy initially, but this also didn't work, as often state that used to be null, might become an object later etc (eg. when loading something from the internet null -> person) or object might become null (eq. selectedItemObject -> null)


Notes:

In the case of primitives, there is probably no way to optimize other than isEqual = a === b which seems fine for me for primitives.

Support for primitives is rather a consistent interface API when working with proxyequal

Cannot read property 'name' of undefined

const name = constructor.name;

constructor could be undefined, for example

Object.create(null)

In my case graphql package could return such object.
Something like

export function shouldInstrument(obj) {
  const name = obj.constructor === undefined && typeof obj === 'object ? 'Object' : constructor.name;
}

should fix it

When proxyShallow should be used?

If I understand correctly it's just a slightly better version of shallowEqual but with the cost of proxy usage.
With consideration of the proxy cost can I assume that it always works faster than shallowEqual?
Why would I want to use proxyShalloow instead of proxyEqual?

Package violates common content security policy rules

In shouldInstrument.js, this package finds the global object by evaluating:

const globalObj = Function("return this")();

A lot of sites operate a content security policy which does not allow evaluation of strings containing JS. It would be nice if we got the global some other way, though some stack overflow discussions reveal this is actually non trivial.

However, there are (not so great) alternatives, which I think would work here without violating common CSP's. Since this package is used as a dep in popular libs like react-memoize, I think it would be a useful change to make it applicable to more audiences :).

Thanks for open sourcing this! :)

Merge Shallow and Proxy Equal

Having the same parent object(shallow equal), there is no need to compare nested information.
EqualProxy should check reference-equality of parent objects, and don't drill down into children if it is the same.
Having this logic it is possible to archive the same speed as shallow equal, and the same accuracy as proxy equal.

ProxyPolyfill is unconditionally imported

The current implementation always imports ProxyPolyfill:

import ProxyPolyfill from './proxy-polyfill';
import {getCollectionHandlers, shouldInstrument} from "./shouldInstrument";
const hasProxy = typeof Proxy !== 'undefined';
const ProxyConstructor = hasProxy ? Proxy : ProxyPolyfill();

However, that polyfill is 6K before minification, which looks like it's about 1/3 of the total source size.

Per https://bundlephobia.com/[email protected] , the deployed size is 8.3K min and 3K min+gz. If the polyfill was only conditionally imported (or even better, made a separate thing that users could import and enable themselves if needed), that size could probably shrink noticeably.

An in-range update of codecov is breaking the build 🚨


🚨 Reminder! Less than one month left to migrate your repositories over to Snyk before Greenkeeper says goodbye on June 3rd! πŸ’œ πŸššπŸ’¨ πŸ’š

Find out how to migrate to Snyk at greenkeeper.io


The devDependency codecov was updated from 3.6.5 to 3.7.0.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

codecov is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ ci/circleci: Build Error: Your tests failed on CircleCI (Details).

Commits

The new version differs by 11 commits.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

An in-range update of babel7 is breaking the build 🚨


🚨 Reminder! Less than one month left to migrate your repositories over to Snyk before Greenkeeper says goodbye on June 3rd! πŸ’œ πŸššπŸ’¨ πŸ’š

Find out how to migrate to Snyk at greenkeeper.io


There have been updates to the babel7 monorepo:

    • The devDependency @babel/cli was updated from 7.8.4 to 7.10.0.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

This monorepo update includes releases of one or more dependencies which all belong to the babel7 group definition.

babel7 is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ ci/circleci: Build Error: Your tests failed on CircleCI (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Performance Improvements

Hey Anton,

I've been looking over the code and spotted a few areas where we might be able to improve performance.

There's some small ideas I had based on chip architecture that should speed up loops (looping backwards is faster than forwards), as well as my understanding of how native methods have been implemented into the browser & what their performance is capable of (filter is slow)

Mind if I open a PR? Also, do you know if any specific areas where bottlenecks are occurring?

Would love to boost the benchmarks if possible :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.