为什么setState导致无限循环?

时间:2019-09-14 19:36:11

标签: javascript reactjs

我有一些代码可以从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'

4 个答案:

答案 0 :(得分:1)

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

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

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

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

答案 1 :(得分:1)

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

所以在您的代码中发生的情况如下:

  1. 您的组件是第一次呈现
  2. useEffect运行
  3. 您将获得一个随机用户和setState
  4. 您的组件重新呈现
  5. useEffect再次运行
  6. ...

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

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

答案 2 :(得分:1)

您应该添加an empty dependency array to the useEffect以防止其循环:

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

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

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

答案 3 :(得分:1)

更改此

  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)
}

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