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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

All onChange functions will be called for multiple Fullscreen elements when one is interacted with. #27

Closed
ajbogh opened this issue Dec 23, 2019 · 9 comments
Labels

Comments

@ajbogh
Copy link

ajbogh commented Dec 23, 2019

I have multiple video components on my webpage, each with a Fullscreen wrapper and an onChange event. Using the code example from the README, when one of these videos is clicked, all of the onChange events fire.

Another bug found that might be related to this is the Fullscreen component will create the fullscreen container and add the classes, however it doesn't resize the video or call the video's requestFullscreen method.

Besides that bug, when the video's minimize button is clicked all of the onClick handlers fire at once, and they all get the false isFull property, but as soon as the isFull state is updated another render cycle will call the onChange event and reset the camera to isFull=true, while the others get isFull=false.

To workaround the problem, I have to add code to check the fullscreen element via a class selector, and then determine if isFull is true and the fullscreen element is not found.

I should state that the exitFullscreen() code below just calls document.exitFullscreen();.

// This component is rendered 5 times with different camera info
export function Camera (props) {
  ...
  const [isFull, setIsFull] = useState();

  <Fullscreen 
          enabled={isFull}
          onChange={isFull => {
            // hack
            const fullscreenVideoElem = document.querySelector('.fullscreen-enabled > video');

            if (!isFull && fullscreenVideoElem && fullscreenVideoElem.id === camera.name) {
              exitFullScreen();
              setIsFull(isFull);
            } else if (isFull && !fullscreenVideoElem) {
              exitFullScreen();
              setIsFull(false);
            } else {
              setIsFull(isFull);
            }
          }}
        >

Here is a log of the maximize and minimize events for one camera click:

// CameraName isFull
Backyard false
Driveway false
Driveway 2 false
Hallway true <== maximizes hallway container, applies classes, does not make video full screen (bug)
Webcam false

This the the log for the minimize button click on the video:

Backyard false
Driveway false
Driveway 2 false
Hallway false <== minimizes hallway, calls setIsFull(false)
Webcam false
Backyard false <== re-render due to setIsFull() hook
Driveway false
Driveway 2 false
Hallway true <== this re-maximizes the hallway but doesn't apply fullscreen classes (good!)
Webcam false
Backyard false <== another render due to re-maximizing
Driveway false
Driveway 2 false
Hallway false <== my CSS selector hack forces the hallway to minimize again
Webcam false

Bug: Multiple onChange handlers are executed when one element is interacted with.

Expected results: Only the onChange event wrapping the selected element should be fired.

Actual results: All of the onChange events are fired multiple times, at least once per setState.

@pomle
Copy link
Contributor

pomle commented Jun 3, 2020

Thank you for the report! Sorry for the very late reply. Will try to look into this soon.

@pomle
Copy link
Contributor

pomle commented Jul 11, 2020

A fix for the multiple callbacks is to save the state for each fullscreen element.

This may be needed but could be abstracted away.

function App() {
  const [first, setFirst] = useState(false);
  const [second, setSecond] = useState(false);

  return (
    <div className="App">
      <button onClick={() => setFirst(true)}>
        First
      </button>

      <button onClick={() => setSecond(true)}>
        Second
      </button>

      <Fullscreen
        enabled={first}
        onChange={setFirst}
      >
        <div className="full-screenable-node" style={{background: "red"}}>
          First
        </div>
      </Fullscreen>

      <Fullscreen
        enabled={second}
        onChange={setSecond}
      >
        <div className="full-screenable-node" style={{background: "green"}}>
          Second
        </div>
      </Fullscreen>
    </div>
  );
}

@pomle
Copy link
Contributor

pomle commented Jul 11, 2020

Here is a wrapped implementation that abstracts away with hooks and handle.

import React, {useCallback, useState} from 'react';
import Fullscreen from "react-full-screen";
import './App.css';

function useFullscreenHandle() {
  const [enabled, setEnabled] = useState(false);

  const enter = useCallback(() => setEnabled(true), []);
  const exit = useCallback(() => setEnabled(false), []);

  return {
    enabled,
    enter,
    exit,
    setEnabled
  };
}

function FullscreenWrap({handle, children}) {
  return <Fullscreen
    enabled={handle.enabled}
    onChange={handle.setEnabled}
  >
    {children}
  </Fullscreen>
}

function App() {
  const screen1 = useFullscreenHandle();
  const screen2 = useFullscreenHandle();

  return (
    <div className="App">
      <button onClick={screen1.enter}>
        First
      </button>

      <button onClick={screen2.enter}>
        Second
      </button>

      <FullscreenWrap handle={screen1}>
        <div className="full-screenable-node" style={{background: "red"}}>
          First
        </div>
      </FullscreenWrap>

      <FullscreenWrap handle={screen2}>
        <div className="full-screenable-node" style={{background: "green"}}>
          Second
        </div>
      </FullscreenWrap>
    </div>
  );
}

export default App;

@pomle
Copy link
Contributor

pomle commented Jul 17, 2020

@ajbogh I have published a prerelease of 0.3.x version.
https://www.npmjs.com/package/react-full-screen/v/0.3.1-0

It works only with React 16.8+ and uses hooks. Feel free to try this version out.

@pomle pomle mentioned this issue Jul 17, 2020
@Nishchit14
Copy link

Uncaught ReferenceError: useFullscreenHandle is not defined

Using https://www.npmjs.com/package/react-full-screen/v/0.3.1-0

image

@pomle
Copy link
Contributor

pomle commented Jul 20, 2020

@Nishchit14 it looks like you have not imported the hook or not using the exported name. Unfortunately there is a typo in the example it looks like. The import should be

import { FullScreen, useFullScreenHandle } from "react-full-screen";

Also make sure you use the hook according to the examples in the readme.

function App() {
  const handle = useFullscreenHandle();
 
  return (
    <div>
      <button onClick={handle.enter}>
        Enter fullscreen
      </button>
 
      <FullScreen handle={handle}>
        Any fullscreen content here
      </FullScreen>
    </div>
  );
}

Sorry for the confusion and thanks for helping out testing.

@ajbogh
Copy link
Author

ajbogh commented Jul 21, 2020

Thanks @pomle. I tested the code and it looks great. Here's a codesandbox that works based on the original problem.

https://codesandbox.io/s/prod-platform-micsb?file=/src/App.js

Since you aren't allowed to use fullscreen in the sandbox, you can grab the frame's URL and go directly to that page (or press the Open in Window button). Open the console to see the messages when an onChange event happens. Below is the link to frame for the above sandbox.

https://micsb.csb.app/

@Nishchit14
Copy link

It's working now... Thanks, @pomle, and @ajbogh

@pomle pomle added the bug label Jul 28, 2020
@pomle
Copy link
Contributor

pomle commented Jul 28, 2020

Looks like everyone's problems have been solved for now. Closing.

@pomle pomle closed this as completed Jul 28, 2020
@pomle pomle pinned this issue Aug 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants