1

TL;DR

"Vanilla-JS app execute the code only if the user is active on the page, whereas React Components execute the code as soon as the component is loaded in the DOM (even if the user is not active on the page)"


I'm working on a React website and creating a SVG animations using "CSS Transitions" which gets triggered by adding a class to the element. Below is the React code

useEffect(() => {    
function startAnimation() {
    var wrapper = document.querySelector('svg#logo');
    wrapper.classList.add('active');
}
}, []);

I initially created this in a Vanilla-JS project (using jQuery's $(document).ready() function) and then converted that into a React Component. But I noticed that the transition in Vanilla-JS app gets initiated only if the page is active, whereas the React Components initiates the transition as soon as the component is loaded in the DOM (even if the user is not active on the page).

I understand that this is because I'm using the useEffect hook, but how can we get the same behavior like $(document).ready() using React?

Below is the Vanialla-JS Code

$(document).ready(function () {
function logoDraw() {
  var wrapper = document.querySelector('svg#logo')
  wrapper.classList.add('active')
}

  setTimeout(logoDraw, 10)
});

Update:

  • Based on the answer from another question, I used the "focus" event listener to start the animation only when the page is in focus. But after doing this, the transition is not getting initiated if I reload the page and stay in the same page. Its only getting initiated if I switch tabs.
  • I found a related question which states that "On first load of the page it already has focus, therefore no event is raised"
Gangula
  • 5,193
  • 4
  • 30
  • 59

1 Answers1

2

After hours of searching online, I finally found the perfect answer in a blog post titled "Harnessing the Page Visibility API with React".

It does either of 2 things:

  1. Detects if page is already active and executes the code immediately (works even when reloaded)
  2. Waits for the tab to become active and executes the code.

The blog goes in detail on how it works and Seth Corker (the owner of the blog) also hosted a demo.


We can use the "Page Visibility API" if the page is active and use the "useEffect" hook to make changes to any DOM elements as needed.

Please find below the a sample copy of the working code from above blog.

function getBrowserVisibilityProp() {
    if (typeof document.hidden !== "undefined") {
        // Opera 12.10 and Firefox 18 and later support
        return "visibilitychange"
    } else if (typeof document.msHidden !== "undefined") {
        return "msvisibilitychange"
    } else if (typeof document.webkitHidden !== "undefined") {
        return "webkitvisibilitychange"
    }
}

function getBrowserDocumentHiddenProp() {
    if (typeof document.hidden !== "undefined") {
        return "hidden"
    } else if (typeof document.msHidden !== "undefined") {
        return "msHidden"
    } else if (typeof document.webkitHidden !== "undefined") {
        return "webkitHidden"
    }
}

function getIsDocumentHidden() {
    return !document[getBrowserDocumentHiddenProp()]
}

function usePageVisibility() {
    const [isVisible, setIsVisible] = React.useState(getIsDocumentHidden())
    const onVisibilityChange = () => setIsVisible(getIsDocumentHidden())

    React.useEffect(() => {
        const visibilityChange = getBrowserVisibilityProp()

        document.addEventListener(visibilityChange, onVisibilityChange, false)

        return () => {
            document.removeEventListener(visibilityChange, onVisibilityChange)
        }
    })

    return isVisible
}

// Above code uses Page_Visibility_API
// Below is a simple React code

const App = (props) => {
    const [counter, setCounter] = React.useState(0);
    const isVisible = usePageVisibility();
    const delay = 1000;


    React.useEffect(() => {
        const timeout = setTimeout(() => {
            if (isVisible) {
                setCounter(counter + 1);
            }
        }, delay);

        return () => {
            clearTimeout(timeout);
        };
    });



    return (
        <div>
            <p>Below number gets updated every {delay / 1000} second only if the page is active</p>
            <span id="output">{counter}</span>
        </div>
    );
};

ReactDOM.render(<App />, document.getElementById("root"));
#output{
  font-size: xx-large
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Gangula
  • 5,193
  • 4
  • 30
  • 59