【问题标题】:reactJS useState infinite loop or not updated at allreactJS useState 无限循环或根本不更新
【发布时间】:2022-11-11 12:22:11
【问题描述】:

我在使 useState 行为正确时遇到问题。

我想隐藏或显示依赖于先前状态的计算的子元素。

当我这样运行它时,它会导致无限重新渲染:

export function PersonWidget(props: Props) {
    const store = useTableState();

    const [positionsVorschlagVisible, setPositionsVorschlagVisible] = React.useState(false);

    var positionsVorschlag = calculatePositionsvorschlag(
        convertTokenToDTOToken(store.previousTokens),
        convertTokenToDTOToken(store.tokens),
        props.tokenId,
        positionsVorschlagVisible.valueOf()
    );

    // this causes infinite loop
    setPositionsVorschlagVisible(visible => 
            (positionsVorschlagVisible && positionsVorschlag.showAction === showAction.keep) || positionsVorschlag.showAction === showAction.show);

    return (
        <div style={styles}>
            {positionsVorschlagVisible &&
                <PositionsVorschlag />
            }             
            <Rente />
        </div>
    )

}

所以我尝试在useEffect 中设置状态,但在初始渲染后不再调用它:

    // not called anymore after initial rendering
    useEffect(() => {
        setPositionsVorschlagVisible(visible => 
            (positionsVorschlagVisible && positionsVorschlag.showAction === showAction.keep) || positionsVorschlag.showAction === showAction.show);
    }, [positionsVorschlagVisible]);

为什么即使positionsVorschlagVisible 已更改,组件也不重新渲染?我使用useEffectuseState 错了吗?

顺便说一句,(我不知道这是否重要),父组件在 mobx 商店中渲染那些 PersonWidgets 依赖于变化:

export const Screen = observer((): React.ReactElement => {
    const store = useTableState();

    var personTokens = store.tokens.filter(token => token.type === 'person');

    return (<div style={ScreenStyles}>
        {personTokens.map((personToken => {
            return (
                <div key={personToken.id} style={{ position: 'absolute', top: personToken.center.y, left: personToken.center.x }}>
                    <PersonWidget tokenId={personToken.id}/>
                </div>
            )
        }))}

    </div>)

});

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    我认为您必须将 setPositionsVorschlagVisible() 添加到 useEffect 中,否则当状态更改时,将始终再次调用 setPositionsVorschlagVisible()。

    【讨论】:

      【解决方案2】:

      确保您不会从 useEffect 中的依赖数组中更改值/s 这导致循环

       useEffect(() => {
          setPositionsVorschlagVisible(visible => 
              (positionsVorschlagVisible && positionsVorschlag.showAction === showAction.keep) || positionsVorschlag.showAction === showAction.show);
      }, [positionsVorschlagVisible]); <--- this is updating after the useEffect is compleated because useState is asynchronous
      

      【讨论】:

        【解决方案3】:

        也许你可以检查一下

        visible => (positionsVorschlagVisible && positionsVorschlag.showAction === showAction.keep) || positionsVorschlag.showAction === showAction.show);
        

        这个箭头函数真的会改变 positionsVorschlagVisible 的值吗?

        如果你确定它是一个不同的值,那么这也会导致无限循环

         // not called anymore after initial rendering
            useEffect(() => {
                setPositionsVorschlagVisible(visible => 
                    (positionsVorschlagVisible && positionsVorschlag.showAction === showAction.keep) || positionsVorschlag.showAction === showAction.show);
            }, [positionsVorschlagVisible]);
        

        推荐

        想想你的positionsVorschlagVisible 的依赖。并将setPositionsVorschlagVisible 添加到其useEffect() 的依赖项中。像这样

            useEffect(() => {
                setPositionsVorschlagVisible(visible => 
                    (positionsVorschlagVisible && positionsVorschlag.showAction === showAction.keep) || positionsVorschlag.showAction === showAction.show);
               // ? dependency of PositionsVorschlagVisible
            }, [dependencyOfPositionsVorschlagVisible]);
        

        一些参考:
        One way data flow
        stack overflow's other answer

        【讨论】:

          【解决方案4】:

          最后我找到了解决我的问题的方法,但我不知道是否应该使用 React。

          使props 依赖useEffect 做到了。有趣的是,这与我正在更改的值无关,它只是渲染的 PersonWidget 的标识并持有tokenId。 (见父组件)

          有人可以解释一下吗?是不是因为我有同一个组件的多个渲染,它“只是”一个函数而不是一个类实例?

          PersonWidget.tsx

          interface Props {
              tokenId: number;
          }
          
          export function PersonWidget(props: Props) {
              console.log('PersonWidget ' + props.tokenId + " rendering");
              
              const store = useTableState();
              const [positionsVorschlagVisible, setPositionsVorschlagVisible] = useState(false);
          
              var positionsVorschlag = calculatePositionsvorschlag(
                  convertTokenToDTOToken(store.previousTokens),
                  convertTokenToDTOToken(store.tokens),
                  props.tokenId,
                  positionsVorschlagVisible
              );
          
              useEffect(() =>  {
                  setPositionsVorschlagVisible((positionsVorschlagVisible && positionsVorschlag.showAction === showAction.keep) || positionsVorschlag.showAction === showAction.show);
              }, [props]);  ///// <----- THIS was the missing piece!!
          
             return (
                  <div>
                      {positionsVorschlagVisible &&
                          <PositionsVorschlag />
                      }
                      <Rente />
                  </div>
              )
          }
          

          父组件:

          //[...]
           return (<div>
                  {personTokens.map((personToken => {
                      return (
                          <div key={personToken.id}>
                              <PersonWidget tokenId={personToken.id} />
                          </div>
                      )
                  }))}
          
              </div>)
          //[...]
          

          【讨论】:

            猜你喜欢
            • 2020-12-14
            • 1970-01-01
            • 2020-10-16
            • 2021-08-11
            • 2021-11-11
            • 2020-10-24
            • 2020-02-21
            • 1970-01-01
            • 2016-10-22
            相关资源
            最近更新 更多