【发布时间】:2022-04-17 19:49:28
【问题描述】:
我在 ReactJS 中有一个网站。每当我的标签成为焦点或隐藏时,我都想得到回调。为此,我遇到了 Page Visibility API,但我无法弄清楚如何在 ReactJS 中使用它。
我应该在哪个生命周期方法中为此注册回调?
【问题讨论】:
标签: reactjs
我在 ReactJS 中有一个网站。每当我的标签成为焦点或隐藏时,我都想得到回调。为此,我遇到了 Page Visibility API,但我无法弄清楚如何在 ReactJS 中使用它。
我应该在哪个生命周期方法中为此注册回调?
【问题讨论】:
标签: reactjs
从 React 16.8 开始使用钩子构建它
import React, { useEffect } from "react";
// User has switched back to the tab
const onFocus = () => {
console.log("Tab is in focus");
};
// User has switched away from the tab (AKA tab is hidden)
const onBlur = () => {
console.log("Tab is blurred");
};
const WindowFocusHandler = () => {
useEffect(() => {
window.addEventListener("focus", onFocus);
window.addEventListener("blur", onBlur);
// Calls onFocus when the window first loads
onFocus();
// Specify how to clean up after this effect:
return () => {
window.removeEventListener("focus", onFocus);
window.removeEventListener("blur", onBlur);
};
}, []);
return <></>;
};
export default WindowFocusHandler;
【讨论】:
onFocus。
这应该可行:
componentDidMount() {
window.addEventListener("focus", this.onFocus)
}
componentWillUnmount() {
window.removeEventListener("focus", this.onFocus)
}
onFocus = () => {
//
}
编辑:“模糊”也是如此,它应该适用于选项卡被隐藏时。
检查@Assaf 的答案以了解钩子的使用情况。
【讨论】:
window.addEventListener("focus", this.onfocus)那样绑定它。解除绑定也是如此
没有可靠的方法来检查它,所以你需要将几种方法组合在一起。这是 react-hooks 的上下文
import React, { useState, useEffect } from 'react'
export const WindowContext = React.createContext(null)
export const WindowContextProvider = props => {
const [windowIsActive, setWindowIsActive] = useState(true)
function handleActivity(forcedFlag) {
if (typeof forcedFlag === 'boolean') {
return forcedFlag ? setWindowIsActive(true) : setWindowIsActive(false)
}
return document.hidden ? setWindowIsActive(false) : setWindowIsActive(true)
}
useEffect(() => {
const handleActivityFalse = () => handleActivity(false)
const handleActivityTrue = () => handleActivity(true)
document.addEventListener('visibilitychange', handleActivity)
document.addEventListener('blur', handleActivityFalse)
window.addEventListener('blur', handleActivityFalse)
window.addEventListener('focus', handleActivityTrue )
document.addEventListener('focus', handleActivityTrue)
return () => {
window.removeEventListener('blur', handleActivity)
document.removeEventListener('blur', handleActivityFalse)
window.removeEventListener('focus', handleActivityFalse)
document.removeEventListener('focus', handleActivityTrue )
document.removeEventListener('visibilitychange', handleActivityTrue )
}
}, [])
return <WindowContext.Provider value={{ windowIsActive }}>{props.children}</WindowContext.Provider>
}
【讨论】:
removeEventListeners 中与内联添加的相同功能。
document.addEventListener('visibilitychange', handleActivity)。否则,活动不会更新,例如,如果您打开控制台导致窗口模糊,则缩小选项卡,然后再次最大化。
我找到了This library。这可能会有所帮助。
这是我将如何使用它来解决您的问题
import React from 'react';
import PageVisibility from 'react-page-visibility';
class YourComponent extends React.Component {
state = {
isWindowInFocus: true,
}
componentDidMount() {
const { isWindowInFocus } = this.props;
if (!isWindowInFocus) {
// do something
}
}
listentoWindow = isVisible => {
this.setState({
isWindowInFocus: isVisible,
});
}
render() {
return (
<PageVisibility onChange={this.listentoWindow}>
<div>
Your component JSX
</div>
</PageVisibility>
);
}
}
【讨论】:
这些都不能很好地满足我的需要,这是一种检测用户是否在选项卡之间切换或通过双击任务栏中的图标最小化浏览器的方法。
他们要么启动了多次,但确实注册了正确的状态,从任务栏图标最小化时没有工作,或者只是没能一个接一个地跟上多个操作。
看到每次焦点改变时我都需要发出服务器请求,上述情况有点“不”。
这就是我所做的:
const DetectChatFocus = () => {
const [chatFocus, setChatFocus] = useState(true);
useEffect(() => {
const handleActivityFalse = () => {
setChatFocus(false);
serverRequest(false);
};
const handleActivityTrue = () => {
setChatFocus(true);
serverRequest(true);
};
window.addEventListener('focus', handleActivityTrue);
window.addEventListener('blur', handleActivityFalse);
return () => {
window.removeEventListener('focus', handleActivityTrue);
window.removeEventListener('blur', handleActivityFalse);
};
}, [chatFocus]);
};
export default DetectChatFocus;
目前这似乎工作得很好,在 Chrome 和 Firefox 上都经过测试,你需要做的就是在一个主要组件中或任何你需要的地方初始化它,它会跟踪所有这些场景的窗口焦点,它会每个操作只发出一个服务器请求。
【讨论】:
更完整和优化的钩子:
import React, { useState, useEffect } from 'react'
import _ from 'lodash'
export default function useIsWindowFocused(): boolean {
const [windowIsActive, setWindowIsActive] = useState(true)
const handleActivity = React.useCallback(
_.debounce(
(e: { type: string }) => {
if (e?.type == 'focus') {
return setWindowIsActive(true)
}
if (e?.type == 'blur') {
return setWindowIsActive(false)
}
if (e?.type == 'visibilitychange') {
if (document.hidden) {
return setWindowIsActive(false)
} else {
return setWindowIsActive(true)
}
}
},
100,
{ leading: false },
),
[],
)
useEffect(() => {
document.addEventListener('visibilitychange', handleActivity)
document.addEventListener('blur', handleActivity)
window.addEventListener('blur', handleActivity)
window.addEventListener('focus', handleActivity)
document.addEventListener('focus', handleActivity)
return () => {
window.removeEventListener('blur', handleActivity)
document.removeEventListener('blur', handleActivity)
window.removeEventListener('focus', handleActivity)
document.removeEventListener('focus', handleActivity)
document.removeEventListener('visibilitychange', handleActivity)
}
}, [])
return windowIsActive
}
【讨论】:
我觉得看document.visibilityState的直接状态更简单,因为它返回'visible'或'hidden':
const [visibilityState, setVisibilityState] = useState('visible')
useEffect(() => {
setVisibilityState(document.visibilityState)
}, [document.visibilityState])
【讨论】:
现代钩子:
import { useCallback, useEffect, useState } from "react";
const useTabActive = () => {
const [visibilityState, setVisibilityState] = useState(true);
const handleVisibilityChange = useCallback(() => {
setVisibilityState(document.visibilityState === 'visible');
}, []);
useEffect(() => {
document.addEventListener("visibilitychange", handleVisibilityChange)
return () => {
document.removeEventListener("visibilitychange", handleVisibilityChange)
}
}, []);
return visibilityState;
}
export default useTabActive;
【讨论】: