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(()=>{},[])
    -它将调用一次

  • 通过数组deps,每当数组依赖项更改时,它将在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} />
        );
    
    }