Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

A more robust condition for 'setUseProxies' #514

Closed
ilatif opened this issue Jan 21, 2020 · 14 comments
Closed

A more robust condition for 'setUseProxies' #514

ilatif opened this issue Jan 21, 2020 · 14 comments

Comments

@ilatif
Copy link

ilatif commented Jan 21, 2020

馃殌 Feature Proposal

Currently immer is checking whether to useProxies or not based on the following condition:

typeof Proxy !== "undefined" && typeof Proxy.revocable !== "undefined" && typeof Reflect !== "undefined"

This condition could be improved such that it also checks whether Proxy is native or not in-order to overcome unexpected errors which might arise when someone is using both immer and proxy-polyfill.

Motivation

I recently came across an issue on IE11 where despite my assumption that immer would work on, I was getting Proxy polyfill does not support trap 'has' error. On investigation I found that this issue is coming an external library which is using proxy-polyfill which is populating/assigning its polyfill globally to window object and since my app is an Angular app (Angular 8) and which has Reflect polyfill added, the condition for useProxies was evaluating to true which caused immer to use modernProxy instead of the legacyProxy and caused the aforementioned error. We can maybe update the above check to something like the following:

typeof Proxy !== "undefined" && Proxy.toString() === 'function Proxy() { [native code] }' && typeof Proxy.revocable !== "undefined" && typeof Reflect !== "undefined"

In above, we are confirming whether Proxy conforms to the native version provided by the browsers before proceeding forward.

Doing this will eliminate unexpected behaviours if some Proxy polyfill is used.

P.S I used setUseProxies from immer to explicitly override the behaviour, but I guess we can handle this more appropriately internally.

Can this be sovled in user-land code?

Yes

@ilatif
Copy link
Author

ilatif commented Jan 21, 2020

I am happy to open a PR, if you think this ticket makes sense 馃槉.

@oriSomething
Copy link
Contributor

oriSomething commented Jan 23, 2020

I think real feature detection is better, something like in https://kangax.github.io/compat-table/es6/

For example:

eval(`+function test() {
var passed = false;
new Proxy({},{});
// A property cannot be reported as deleted, if it exists as a non-configurable
// own property of the target object.
var proxied = {};
Object.defineProperty(proxied, "foo", { value: 2, writable: true, enumerable: true });
try {
  delete new Proxy(proxied, {
    deleteProperty: function () {
      passed = true;
      return true;
    }
  }).foo;
  return false;
} catch(e) {}
return passed;
}()`);

But I think in one application it make no sense to use both "proxy" and "es5" modes, it just can make more unwanted bugs that hard to find

@oriSomething
Copy link
Contributor

Okay, i've wrapped in not good... but the idea is clear

@ilatif
Copy link
Author

ilatif commented Jan 23, 2020

It's not about using Proxy and es5 together. It's about immer picking appropriate implementation of Proxy. As per my understanding of code, immer determines whether to use available Proxy implementation or use its own implementation which isn't a Proxy polyfill per say. It is just that in non-supporting browsers there might be a Proxy polyfill included which could be used to target a specific scenario and might not work well with immer. This problem can simply be avoided by making sure that immer would only use native Proxy implementation and in-case it's not present, it will fallback to its own implementation instead of a custom Proxy polyfill which might not work well with immer.

@mweststrate
Copy link
Collaborator

My 2 cents: I think we shouldn't be reasoning about whether a polyfill is correct or not, if one polyfill is broken, and the other one isn't, how can we decide whether to use a polyfill or not? Run a full es test suite during startup of the module (since we basically use every Proxy feature in the book)? That doesn't really sound like a plan. I think it is the responsibility of the person that adds the polyfill, to make sure it is a properly working one and if it isn't, disable the proxy usage in Immer.

@ilatif
Copy link
Author

ilatif commented Jan 23, 2020

@mweststrate Thanks for commenting.

You are absolutely right about it being the responsibility of the person to properly handle Proxy usage in immer. It is just that we for sure know that browser's native Proxy is the most accurate and reliable implementation than others and if there is some other implementation besides that, immer could fallback to its own implementation. Asking immer to disable the usage of unwanted Proxy implementations is pretty straight-forward but still vary depending on framework-to-framework. I had to inject my reducers using Dependency Injection in my Angular app to be able to decide and disable unwanted Proxy usage.

In-short, while checking Proxy, if we could just check if that's the native one (using the way I mentioned in the main comment, there might be other ways as well) only then use it and in other scenarios fallback to the internal implementation. Non-native implementations/polyfills tend to be slower than the native one, so its better to use immer's internal implementation if that's the case.

P.S I am thinking of writing a blogpost on how Google's proxy-polyfill conflicted with immer and how to fix in Angular app 馃槉. I thought I should create the ticket so that people know I am pretty serious about getting this fixed 馃槢.

@aleclarson
Copy link
Member

馃帀 This issue has been resolved in version 7.0.0 馃帀

The release is available on:

Your semantic-release bot 馃摝馃殌

@ilatif
Copy link
Author

ilatif commented Jun 10, 2020

@aleclarson Awesome! That's huge 馃殌.

@Justin-CATCH
Copy link

I saw this issue happened again on Safari 14.0.1, throw by immer.

Crashed in non-app: webpack://_N_E/../node_modules/@reduxjs/toolkit/node_modules/immer/dist/immer.esm.js Proxy polyfill does not support trap 'has'

We are using immer 8.0.1. Any idea?

@ilatif
Copy link
Author

ilatif commented Aug 20, 2021

@Justin-CATCH Do you directly or indirectly proxy-polyfill? I was having exact error due to indirectly including it. Did following to solve the issue:

P.S I used setUseProxies from immer to explicitly override the behaviour, but I guess we can handle this more appropriately internally.

@mweststrate
Copy link
Collaborator

as mentioned before proxy-polyfill makes the feature detection pass, but then delivers a broken implementation. So there is little we can do here. proxy-polyfill itself is simply broken (conceptually it is even impossible to polyfill Proxy correctly). So either just remove proxy-polyfill, or explicitly configure Immer to not use proxies.

@ilatif
Copy link
Author

ilatif commented Aug 20, 2021

@mweststrate

as mentioned before proxy-polyfill makes the feature detection pass, but then delivers a broken implementation.

Couldn't agree more 馃憤 .

So either just remove proxy-polyfill, or explicitly configure Immer to not use proxies.

I unfortunately had to go with the latter one because proxy-polyfill was added as an indirect dependency in my case and there was no way I could get around with it. But I am really glad and thankful to you to provide a way to override proxy configuration built-in to the immer.

@Justin-CATCH
Copy link

Thanks all. What is the implication of not using proxy for immer?

@ilatif
Copy link
Author

ilatif commented Aug 23, 2021

Thanks all. What is the implication of not using proxy for immer?

@Justin-CATCH I think I read somewhere in docs that it'd have performance implications. But in all honesty, I didn't see anything performing slow in my app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants