Javascript 功能组件内部的状态初始化(无无限循环)
我有一个功能组件,在其状态下保存自定义视口值,因此它必须使用事件侦听器并测量窗口大小:Javascript 功能组件内部的状态初始化(无无限循环),javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,我有一个功能组件,在其状态下保存自定义视口值,因此它必须使用事件侦听器并测量窗口大小: const AppWrap = () => { // custom vw and vh vars const [vw, setvw] = useState(); const [vh, setvh] = useState(); // gets the inner height/width to act as viewport dimensions (cross-platform ben
const AppWrap = () => {
// custom vw and vh vars
const [vw, setvw] = useState();
const [vh, setvh] = useState();
// gets the inner height/width to act as viewport dimensions (cross-platform benefits)
const setViewportVars = () => {
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// can be accessed in scss as vw(n), vh(n) OR in css as --vw * n, --vh * n
document.documentElement.style.setProperty('--vw', `${viewportWidth / 100}px`);
document.documentElement.style.setProperty('--vh', `${viewportHeight / 100}px`);
// can be accessed in child components as vw * n or vh * n
setvw(viewportWidth / 100);
setvh(viewportHeight / 100);
}
// I'd like to run this function *once* when the component is initialized
setViewportVars();
// add listeners
window.addEventListener('resize', setViewportVars);
window.addEventListener('orientationchange', setViewportVars);
window.addEventListener('fullscreenchange', setViewportVars);
return (
<App vw={vw} vh={vh}/>
);
}
const AppWrap=()=>{
//定制vw和vh VAR
const[vw,setvw]=useState();
const[vh,setvh]=useState();
//获取用作视口尺寸的内部高度/宽度(跨平台优势)
常量setViewportVars=()=>{
const viewportWidth=window.innerWidth;
常量视口高度=window.innerHeight;
//可在scss中以vw(n)、vh(n)或css中以--vw*n、-vh*n的形式访问
document.documentElement.style.setProperty('--vw',`${viewportWidth/100}px`);
document.documentElement.style.setProperty('--vh',`${viewportHeight/100}px`);
//可以在子组件中访问vw*n或vh*n
setvw(视口宽度/100);
setvh(视口高度/100);
}
//我想在初始化组件时运行此函数*一次*
setViewportVars();
//添加侦听器
addEventListener('resize',setViewportVars);
addEventListener('orientationchange',setViewportVars);
addEventListener('fullscreenchange',setViewportVars);
返回(
);
}
上面的代码生成错误:重新渲染的次数太多。React限制渲染的数量以防止无限循环。
我可以在useffect中包装
setViewportVars()
,但我不明白为什么需要这样做。我对函数组件的理解是,它们只在return语句之外运行一次代码,并且只有JSX会在状态更改时重新呈现。因此在您的情况下,基本上是调用函数,它会更新状态,所以,组件将再次加载,函数将调用,基本上进入无限循环
解决方案
可以,因此在useffect中,如果将第二个参数作为空数组传递,它将像componentDidMount一样只调用一次
useEffect(() => {
setViewportVars()
}, [])
所以如果你通过第二个论点
useffect(()=>{})
-它每次都会调用useffect(()=>{},[])
-它将调用一次useffect(()=>{
//一些逻辑
},[用户]
您必须使用
useffect
并将空数组作为依赖项传递,因此这只会像componentDidMount
一样执行一次:
useEffect(() => {
setViewportVars();
// add listeners
window.addEventListener('resize', setViewportVars);
window.addEventListener('orientationchange', setViewportVars);
window.addEventListener('fullscreenchange', setViewportVars);
}, []);
const AppWrap=()=>{
//定制vw和vh VAR
const[vw,setvw]=useState();
const[vh,setvh]=useState();
//获取用作视口尺寸的内部高度/宽度(跨平台优势)
const setViewportVars=useCallback(()=>{
const viewportWidth=window.innerWidth;
常量视口高度=window.innerHeight;
//可在scss中以vw(n)、vh(n)或css中以--vw*n、-vh*n的形式访问
document.documentElement.style.setProperty('--vw',`${viewportWidth/100}px`);
document.documentElement.style.setProperty('--vh',`${viewportHeight/100}px`);
//可以在子组件中访问vw*n或vh*n
setvw(视口宽度/100);
setvh(视口高度/100);
}, []);
useffect(()=>{
addEventListener('resize',setViewportVars);
addEventListener('orientationchange',setViewportVars);
addEventListener('fullscreenchange',setViewportVars);
return()=>{
removeEventListener('resize',setViewportVars);
removeEventListener('orientationchange',setViewportVars);
removeEventListener('fullscreenchange',setViewportVars);
}
}, []);
useffect(()=>{
//我想在初始化组件时运行此函数*一次*
setViewportVars();
}, []);
返回(
);
}
关于为什么需要使用effect()来防止无限重渲染的答案:
在您的情况下,当组件加载时,它会调用函数,该函数会再次更改状态,因此它会再次调用组件,从而更改状态。按顺序转到无限条件。为了避免这种情况,您需要使用效果,添加了答案,请检查:)1。用useCallback 2包装您的回调。在use effect中绑定事件,在return函数2中解除绑定。从useEffect调用setViewportVars,将空数组作为dependciesI。我知道这将如何工作,但它与文档中的建议不符。例如:“回调中引用的每个值也应该出现在dependencies数组中”()关于空数组dep的类似建议可以在这里找到:在进一步阅读之后,我认为正确的方法实际上是将setViewportVars
拉入useffect
中,而不是将其包装在useCallback
()中。如果我删除
(即使AppWrap呈现为空),我将获得相同的循环行为。因此,我不确定您在两个组件之间绘制的顺序/连接是否准确。这是正确的-谢谢!在详细阅读了这篇文章之后,我还想指出,与其列出一个空的dep数组,不如将setViewportVars
移到useffect()
const AppWrap = () => {
// custom vw and vh vars
const [vw, setvw] = useState();
const [vh, setvh] = useState();
// gets the inner height/width to act as viewport dimensions (cross-platform benefits)
const setViewportVars = useCallback(() => {
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// can be accessed in scss as vw(n), vh(n) OR in css as --vw * n, --vh * n
document.documentElement.style.setProperty('--vw', `${viewportWidth / 100}px`);
document.documentElement.style.setProperty('--vh', `${viewportHeight / 100}px`);
// can be accessed in child components as vw * n or vh * n
setvw(viewportWidth / 100);
setvh(viewportHeight / 100);
}, []);
useEffect(() => {
window.addEventListener('resize', setViewportVars);
window.addEventListener('orientationchange', setViewportVars);
window.addEventListener('fullscreenchange', setViewportVars);
return () => {
window.removeEventListener('resize', setViewportVars);
window.removeEventListener('orientationchange', setViewportVars);
window.removeEventListener('fullscreenchange', setViewportVars);
}
}, []);
useEffect(() => {
// I'd like to run this function *once* when the component is initialized
setViewportVars();
}, []);
return (
<App vw={vw} vh={vh} />
);
}