React: Accessing window properties inside useEffect leads to "Invalid calling object" error in IE 11

Created on 20 Nov 2018  路  8Comments  路  Source: facebook/react

Hello,

I've noticed in two specific use cases that accessing window properties inside a useEffect leads to the error "Invalid calling object" in Internet Explorer 11.

I have not been able to isolate a minimum demo of the problem unfortunately.

First problem - window scroll
useEffect(() => { window.scroll(0, 0); }, [])

Second problem - Apollo client polling
useEffect(() => { startPolling(120000); return stopPolling; }, []);
https://github.com/apollographql/apollo-client/blob/6f579ea2cdf8c78acb004a4457a7a6764d64091e/packages/apollo-client/src/scheduler/scheduler.ts The start polling method internally uses setInterval.

In both cases, replacing the useEffect by lifecycles removes the error.

React version: 16.7.0-alpha.2

Needs More Information

All 8 comments

Can you please provide a complete reproducing case? (I understand you haven't been able to yet, but this is unfortunately too vague to be actionable.)

The problem seems fixed if I call useLayoutEffect instead of useEffect. I did not manage to get a complete reproducing case, and I don't know why it's only failing on IE so I'll close this issue.

On an API perspective, I find this distinction to be quite dangerous - if you refer to my example with apollo client, I should not have to dig into libraries to see how they are implemented.

But the useMutationEffect and useLayoutEffect are probably among the things that make the alpha an alpha!

I don鈥檛 understand how what you鈥檙e complaining about is a failure of the API.

If useEffect is broken then we need to fix it. We can鈥檛 however unless you provide a reproducing case to run.

If it鈥檚 not broken then presumably the code that runs there doesn鈥檛 work in IE for some reason. Then that code needs fixing.

Neither is a problem of useEffect API.

To be clear they differ in when they run which is absolutely intentional. Because running effects later lets us reduce performance issues that are caused by running too much code in lifecycle methods. If we just make useLayoutEffect behavior default we haven鈥檛 solved the problem and are back to square one.

Of course it鈥檚 annoying to dig into bugs. But we need to figure out whose bug it is, and make a proper fix for it.

Ok so if I understand you correctly the useLayoutEffect behaves strictly like a lifecycle.

I completely get your point regarding useEffect vs useLayoutEffect and I sincerely wish we identified whether the problem comes from react or from our code base.

We've had someone spend 3 full days trying to reproduce the problem and unfortunately we don't have the resources to continue investigating full time. The only thing we found is that it never happens in development mode and that it's fixed when replaced by a lifecycle.

And the main problem here is that it happens only in IE as we don't have any windows setup.

Fair enough 馃檪 If you do find a reduced case we鈥檇 appreciate sharing it.

All useEffect does is run the work a bit later, when the browser isn鈥檛 busy. It鈥檚 hard to guess which APIs don鈥檛 work in this case in IE or why.

@gaearon we've managed to narrow the scope of the problem.
It happens when the code is build with devtools: eval-source-map or eval. it's working with source-map.

  • Edge is also affected

It seems to be related to a known IE/Edge bug
https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8282928/
https://medium.com/@CWMma/doing-something-simple-5cdc65c416ba
https://github.com/18F/calc/issues/1976

We are seeing the same issue. currently we will not be able to get a repro for a few weeks.

But i can confirm that the bug happens under these conditions:

  1. Have a custom hook that uses window properties
import React from 'react';

/**
 * Determine if the user has "reduce-motion" enabled in their browser.
 *
 * @example
 * const isReducedMotion = useReduceMotion();
 */
export const useReduceMotion = () => {
  const [reduceMotion, setReduceMotion] = React.useState(
    window.matchMedia('(prefers-reduced-motion: reduce)').matches
  );

  // With either useEffect or useLayoutEffect this breaks. I can even comment
  // out the entire inside of the effect and the error happens.
  React.useEffect(() => {
    const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');

    /** Ran when the user changes this setting. */
    const changeReduceMotion = () => setReduceMotion(!reduceMotion);

    mediaQuery.addListener(changeReduceMotion);

    return () => {
      mediaQuery.removeListener(changeReduceMotion);
    };
  }, [reduceMotion]);

  return reduceMotion;
};
  1. Have devtool: 'eval-source-map' somewhere in a webpack build

  2. Open IE

Screen Shot 2019-08-14 at 3 03 35 PM

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sebmarkbage picture sebmarkbage  路  136Comments

acdlite picture acdlite  路  83Comments

gaearon picture gaearon  路  104Comments

gaearon picture gaearon  路  133Comments

wohali picture wohali  路  128Comments