React-testing-library: Rendering Using 'muted' attribute in a video tag prints a warning about flushing updates to the console.

Created on 29 Aug 2019  路  9Comments  路  Source: testing-library/react-testing-library

  • react-testing-library version: 9.1.1
  • react version: 16.9.0
  • node version:
  • npm (or yarn) version:

Relevant code or config:

Code Sandbox: https://codesandbox.io/embed/agitated-snyder-mvvgi

Component:

import React from "react";

const App = () => {
  return (
    <div className="App">
      <h1>Hello Scout</h1>
      <h2>Start editing to see some magic happen!</h2>
      <video muted />
    </div>
  );
};

export default App;

Test:

import React from "react";
import { render, wait } from "@testing-library/react";
import App from "./App";

describe("App", () => {
  it("renders", async () => {
    const { container } = render(<App />);
    await wait(() => expect(container.textContent).toContain("Scout"));
  });
});

What you did:

I noticed a waring in my logs when rendering a video element with the muted attribute:

Warning: unstable_flushDiscreteUpdates: Cannot flush updates when React is already rendering.
    in video (at App.js:15)
    in div (at App.js:12)
    in App (at App.test.js:7)
needs investigation

Most helpful comment

I've run into the same issue. The problem is that setting the muted property on a HTMLMediaElement leads to an event being fired, which in turn leads to flushDiscreteUpdatesIfNeeded being called.

At first, I thought we should be setting <videoRef>.defaultMuted = true instead, but while that sets the muted attribute on the video element in the DOM, it doesn't actually mute sound. On the other hand, setting muted in react doesn't set the muted attribute in the DOM.

Unfortunately, I haven't found a simple fix (and there are other issues open about this, e.g. this). In case you're interested, my workaround is to use my own wrapper for the render function:

const renderIgnoringUnstableFlushDiscreteUpdates = (component: React.ReactElement) => {
  // tslint:disable: no-console
  const originalError = console.error;
  const error = jest.fn();
  console.error = error;
  const result = render(component);
  expect(error).toHaveBeenCalledTimes(1);
  expect(error).toHaveBeenCalledWith("Warning: unstable_flushDiscreteUpdates: Cannot flush updates when React is already rendering.%s", expect.any(String));
  console.error = originalError;
  // tslint:enable: no-console
  return result;
};

All 9 comments

Verified. I'm not sure what this could be! Would you mind digging a little deeper and see if you can figure out what's causing this warning and whether there's anything we can do within React Testing Library? Perhaps you could ask on https://spectrum.chat/testing-library/help-react

@kentcdodds Of course, I'd love to take a look around and see what I can see, though it might not be until after the long weekend here in the US that I am able to dive in.

I've run into the same issue. The problem is that setting the muted property on a HTMLMediaElement leads to an event being fired, which in turn leads to flushDiscreteUpdatesIfNeeded being called.

At first, I thought we should be setting <videoRef>.defaultMuted = true instead, but while that sets the muted attribute on the video element in the DOM, it doesn't actually mute sound. On the other hand, setting muted in react doesn't set the muted attribute in the DOM.

Unfortunately, I haven't found a simple fix (and there are other issues open about this, e.g. this). In case you're interested, my workaround is to use my own wrapper for the render function:

const renderIgnoringUnstableFlushDiscreteUpdates = (component: React.ReactElement) => {
  // tslint:disable: no-console
  const originalError = console.error;
  const error = jest.fn();
  console.error = error;
  const result = render(component);
  expect(error).toHaveBeenCalledTimes(1);
  expect(error).toHaveBeenCalledWith("Warning: unstable_flushDiscreteUpdates: Cannot flush updates when React is already rendering.%s", expect.any(String));
  console.error = originalError;
  // tslint:enable: no-console
  return result;
};

Interesting. I think this is out of scope for React Testing Library as it appears to be something with React specifically. If anyone can come up with something we can do to improve things, then please feel free to comment further, but from what I can tell, there's not much we can do.

@chriszwickerocteris thanks! Here's the same code in eslint:

const renderIgnoringUnstableFlushDiscreteUpdates = component => {
    /* eslint-disable no-console */
    const originalError = console.error
    const error = jest.fn()
    console.error = error
    const result = render( component )
    expect( error ).toHaveBeenCalledTimes( 1 )
    expect( error ).toHaveBeenCalledWith( 'Warning: unstable_flushDiscreteUpdates: Cannot flush updates when React is already rendering.%s', expect.any( String ) )
    console.error = originalError
    /* eslint-enable no-console */
    return result
}

Just to confirm that this is a react issue with muted event on videos.

https://github.com/facebook/react/issues/10389

I was facing this warning in react-testing-library. I had been looking for the solution for around 4-5 hours. This works for me.
I would like to add steps in the solution because I was facing how to wrap the render component.

Step 1: add this function at the top of the test file.
const renderIgnoringUnstableFlushDiscreteUpdates = (component) => {
// tslint:disable: no-console
const originalError = console.error;
const error = jest.fn();
console.error = error;
const result = render(component);
expect(error).toHaveBeenCalledTimes(1);
expect(error).toHaveBeenCalledWith(
'Warning: unstable_flushDiscreteUpdates: Cannot flush updates when React is already rendering.%s',
expect.any(String),
);
console.error = originalError;
// tslint:enable: no-console
return result;
};

Step 2: Wrap the render component from the step 1 function:

EX: renderIgnoringUnstableFlushDiscreteUpdates();

Step 3: Add this code in setupTest.js file for run video correctly.

window.HTMLMediaElement.prototype.load = () => {
/* do nothing /
};
window.HTMLMediaElement.prototype.play = () => {
/
do nothing /
};
window.HTMLMediaElement.prototype.pause = () => {
/
do nothing /
};
window.HTMLMediaElement.prototype.addTextTrack = () => {
/
do nothing */
};

For me the following snippet in jest setup is helped:

Object.defineProperty(HTMLMediaElement.prototype, 'muted', {
  set: () => {},
});

My video element usage is:

 <video src={src} autoPlay={true} loop={true} muted={true} playsInline={true} />

this should be fixed along with:
https://github.com/facebook/react/pull/20087

Was this page helpful?
0 / 5 - 0 ratings

Related issues

joshvillahermosa picture joshvillahermosa  路  3Comments

mezei picture mezei  路  3Comments

suresh777 picture suresh777  路  3Comments

jalvarado91 picture jalvarado91  路  3Comments

alejandronanez picture alejandronanez  路  4Comments