繁体   English   中英

为什么setState导致无限循环?

[英]Why is my setState causing an infinite loop?

我有一些代码从firebase数据库中获取用户列表,将其随机化,然后返回一个-但是,当我尝试将状态设置为随机选择的用户时,我遇到了无限循环。

我尝试过将setState()在现在的位置和randomizeUsers函数之间移动,但得到的结果相同

有问题的代码:


  const [state, setState] = useState({
    potentialParnter: null
  });

  useEffect(() => {
    generateRandomPotentialPartner();
    console.log(state.potentialParnter)
  })

  const getUsers = () => {
    const database = firebase.database()
    const users = database.ref('users');
    return new Promise((resolve, reject) => {
      try {
        users.on('value', function(snapshot) {
            const users = []
            snapshot.forEach(function(childSnapshot) {
              let user = childSnapshot.val();
              users.push(user)
            });
            resolve(users);
        });
       } catch(e) {
           reject(e);
       }
    })   
  }

  async function generateRandomPotentialPartner() {
    try {
      const users = await getUsers();
      const randomUser = randomizeUsers(users)
      setState({
        potentialParnter: randomUser
      })
    } catch (error) {
      console.log(error)
    }
  }

  const randomizeUsers = (users) => {
    const randomPotentialParnter = users[Math.floor((Math.random() * users.length))]
    return randomPotentialParnter
  }

  const potentialParnter = state.potentialParnter ? state.potentialParnter.name : 'loading'

这是因为您的useEffect()挂钩。 每当组件的状态或道具更新时,都会触发useEffect()挂钩。 有点像componentDidUpdate()

由于您正在执行一些逻辑来更新useEffect的状态,因此只会创建一个无限循环。

您可以通过将一个空数组作为第二个参数添加到useEffect()来避免这种情况。 使它实际上与componentDidMount()相同。 因此,其中的逻辑只会运行一次。

  useEffect(() => {
    generateRandomPotentialPartner();
    console.log(state.potentialParnter)
  }, [])

这是因为useEffect :如果不提供第二个参数,则此挂钩将在每次组件重新渲染时运行。

因此,您的代码中发生的情况如下:

  1. 您的组件是第一次渲染
  2. useEffect运行
  3. 您会得到一个随机用户和setState
  4. 您的组件重新渲染
  5. useEffect再次运行
  6. ...

如果您希望它仅在安装组件时运行,请提供一个空数组作为第二个参数:

  useEffect(() => {
    generateRandomPotentialPartner();
    console.log(state.potentialParnter)
  }, []);

您应该useEffect添加一个空的依赖项数组,以防止其循环:

useEffect(() => {
  generateRandomPotentialPartner();
}, []);

如果要记录state更改时的state结果,则可能需要另一个useEffect

useEffect(() => {
  console.log(state.potentialParnter);
}, [state]);

改变这个

  const [state, setState] = useState({
    potentialParnter: null
  });

为此(以便我们稍后可以检查其长度以使用该长度来知道是否重新渲染)

  const [state, setState] = useState({
    potentialParnter: ""
  });

====================================

然后改变这个

  useEffect(() => {
    generateRandomPotentialPartner();
    console.log(state.potentialParnter)
  })

为此(现在我们正在检查其长度是否已更改,然后我们可以重新渲染,否则我们将不重新渲染)

  useEffect(() => {
    generateRandomPotentialPartner();
    console.log(state.potentialParnter)
  }, [state.potentialParnter.length])

=======================================

然后改变这个

try {
  const users = await getUsers();
  const randomUser = randomizeUsers(users)
  setState({
    potentialParnter: randomUser
  })
} catch (error) {
  console.log(error)
}

为此(假设您只希望世代发生一次)

try {
  const users = await getUsers();
  const randomUser = randomizeUsers(users)

  if(state.potentialPartner.length == 0){
  setState({
    potentialParnter: randomUser
  })
}
} catch (error) {
  console.log(error)
}

希望这对您有用。 我对其他答案的解释基于相同的原理,因此无需重复解释。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM