【问题标题】:React - Substitute for `setState` Callback in Functional Components?React - 替代功能组件中的“setState”回调?
【发布时间】:2020-05-30 21:57:52
【问题描述】:

我们已经迁移到“React 功能组件”而不是“基于类的组件”。我找不到 setState callback function 的替代逻辑。即,我有一个带状态的功能组件,我想创建一个事件处理函数,它可以按顺序多次改变状态,需要注意的是我不知道状态的当前值(可能是true/@987654324 @)。下面的例子可能更有意义。

const Example = () => {
  const [ openDoor, setOpenDoor ] = useState(false); 
  // the following handler should swich 'openDoor' state to inverse of
  // current state value. Then after setTimeout duration, inverse it again
  const toggleOpenDoor = () => {
  setOpenDoor(!openDoor);
  // within setTimeout below, '!openDoor' does not work because it still
  // receives the same value as above because of async nature of 
  // state updates
  setTimeout(() => setOpenDoor(!openDoor), 500)
  }
  return(...);
}

在基于class 的组件中,我们有回调参数,它将在上次更新后更新状态。如何使用状态挂钩在上述功能组件中实现相同的功能?

【问题讨论】:

  • 我认为你可以做类似setOpenDoor(prevOpenDoor => !prevOpenDoor)

标签: javascript reactjs react-hooks


【解决方案1】:

我想知道 useEffect 是否是最好的解决方案。特别是当在 useEffect 中调用 setTimeout 时会导致无限循环,因为每次我们调用 setOpenDoor 时,应用都会渲染,然后调用 useEffect 再次调用 setTimeOut,该 setTimeOut 将调用 setOpenDoor 函数...以图形方式:

setTimeout -> setOpenDoor -> useEffect -> setTimeout -> ... hell  

当然,您可以使用与@ksav 建议相同的方式使用带有 useEffect 的 if 语句,但这并不能满足 @Kayote 的一项要求:

我不知道 state 的当前值(可能是真/假)

这是一个无需 useEffect 即可工作并满足上述要求的解决方案:

Code working in codesandbox

在那里,看看这段代码的重要性:

const toggleOpenDoor = () => {
  setOpenDoor(!openDoor);
  setTimeout(() => setOpenDoor(openDoor => !openDoor), 500);
};

由于我们使用 setTimeout,我们需要将回调传递给 setOpenDoor,而不是更新状态。这是因为我们要发送“当前”状态。如果我们改为发送新状态,则在 setTimeOut 处理该状态时,它会发生变化(因为我们在 setTimeOut 使用 setOpenDoor(!openDoor); 执行其回调之前已经完成了它)并且不会进行任何更改。

【讨论】:

    【解决方案2】:

    您可以使用 useEffect 挂钩来查看状态更改发生的时间。

    useEffect(() => { 
      // do something
      console.log('openDoor change', openDoor)
    }, [openDoor]);
    

    【讨论】:

    • 您应该按照 OP 的要求解释如何在 useEffect 中使用超时。
    【解决方案3】:

    我会告诉你,它的工作方式与this.setState 几乎相同,你只需传递一个回调函数,该函数将先前的状态作为参数并返回新的状态(docs)

    const Example = () => {
      const [openDoor, setOpenDoor] = useState(false); 
    
      const toggleOpenDoor = () => {
        setOpenDoor(!openDoor);
        setTimeout(() => setOpenDoor(prevDoor => !prevDoor), 500)
      }
      return(...);
    }
    

    为了让您知道它何时发生变化,您可以使用 useEffect 回调,每次依赖数组中发生变化时都会调用它(docs

    const Example = () => {
      const [openDoor, setOpenDoor] = useState(false); 
    
      useEffect(() => {
        console.log('openDoor changed!', openDoor)
      }, [openDoor])
    
      const toggleOpenDoor = () => {
        setOpenDoor(!openDoor);
        setTimeout(() => setOpenDoor(prevDoor => !prevDoor), 500)
      }
      return(...);
    }
    

    :)

    【讨论】:

      【解决方案4】:

      你可以使用useEffect钩子来实现这个。

      setOpenDoor(!openDoor);
      
      useEffect(() => {
         // Here your next setState function
      }, [openDoor]);
      

      有关钩子的更多信息,请查看https://reactjs.org/docs/hooks-effect.html

      【讨论】:

        【解决方案5】:

        你应该只在useEffect回调中使用setTimeout

        const App = () => {
          const [openDoor, setOpenDoor] = useState(false);
        
          const toggle = () => setOpenDoor(prevOpen => !prevOpen);
        
          useEffect(() => {
            const id = setTimeout(() => toggle(), 1000);
            return () => clearTimeout(id);
          }, [openDoor]);
        
          return <Container>isOpen: {String(openDoor)}</Container>;
        };
        

        【讨论】:

          【解决方案6】:
          import React, { useState, useEffect } from "react";
          
          const Example = () => {
            const [openDoor, setOpenDoor] = useState(false);
            const toggleOpenDoor = () => {
              setOpenDoor(!openDoor);
            };
            useEffect(() => {
              console.log(openDoor);
              if (openDoor) {
                setTimeout(() => setOpenDoor(!openDoor), 1500);
              }
            }, [openDoor]);
            return (
              <>
                <button onClick={toggleOpenDoor}>Toggle</button>
                <p>{`openDoor: ${openDoor}`}</p>
              </>
            );
          };
          
          export default Example;
          

          Codesandbox

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-11-22
            • 1970-01-01
            • 2021-04-20
            • 2020-09-02
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多