【问题标题】:converting class based component with state to functional component with hooks将具有状态的基于类的组件转换为具有挂钩的功能组件
【发布时间】:2020-05-22 21:12:51
【问题描述】:

我有这个带有状态的组件,当用户在线时,它会增加计数。我想把它改成带钩子的功能组件,我在下面做了

class App extends React.Component {
  state = {
    isOnline: true,
    count: 1
  }

  handleOnline = () => {
    if (!this.state.isOnline) {
      this.setState({
        count: this.state.count + 1
      }, () => {
        this.setState({ isOnline: !this.state.isOnline })
      })
    } else {
      this.setState({ isOnline: !this.state.isOnline })
    }

  }
  render() {
    return (
      <div className="App">
        <h1>online ==> {this.state.isOnline ? 'Online' : 'Offline'}</h1>
        <h1>count ==> {this.state.count}</h1>
        <button onClick={this.handleOnline}>Toggle</button>
      </div>
    );

  }
}

这是我用钩子转换成功能组件的过程,

const App = () => {
  const [isOnline, setIsOnline] = useState(true)
  const [count, setCount] = useState(1)

  const handleClick = () => {
    if (!isOnline) {
      setIsOnline(!isOnline)
      setCount(count + 1)
    } else {
      setIsOnline(!isOnline)
    }
  }

  return (
    <div className="App">
      <h1>online ==> {isOnline ? 'Online' : 'Offline'}</h1>
      < h1 > count ==> {count}</h1>
      <button onClick={handleClick}>Toggle</button>
    </div>
  )
}

在基于类的组件中,我已经读到不要一个接一个地使用setState所以我像这样使用this.setState中的回调函数

this.setState({
        count: this.state.count + 1
      }, () => {
        this.setState({ isOnline: !this.state.isOnline })
      })

现在,在功能组件中我已经一个接一个地使用了 setCount 和 setIsOnline 好不好???

const handleClick = () => {
    if (!isOnline) {
      setIsOnline(!isOnline)
      setCount(count + 1)
    } else {
      setIsOnline(!isOnline)
    }

我已经阅读过使用 useEffect 进行回调,但我得到的只是无限循环。即使我的两个组件都工作并给了我想要的结果。我想知道我是否必须使用 useEffect 进行回调,或者我在功能组件中使用钩子的实现是否正确???

【问题讨论】:

    标签: javascript reactjs callback components react-hooks


    【解决方案1】:

    这个实现是正确的,是的,我们不应该一个接一个地设置一个状态,因为 setState 是异步工作的,但是因为你只设置了两个状态,所以没问题。
    虽然您也可以保留一个状态对象而不是两个单独的状态,即

    const [state, setState] = useState({ count: 1, isOnline: true });
    

    然后您可以在一个 setState 中设置两个对象键,例如:

    setState(() => ({
      count: 1,
      isOnline: false,
    }))
    

    同样在基于类的方法中,您使用了回调但实际上并不需要,您可以使用单个 setState 来设置两种状态,即

    this.setState(() => ({
      count: this.state.count + 1, 
      isOnline: !this.state.isOnline ,
    }))
    

    另一个重要提示:

    尝试使用我在上面示例中使用的功能集状态,因为它可以降低陷入 React 状态异步问题的风险。

    【讨论】:

    • 为什么我们不能一个接一个地设置状态?
    • 这并不是说我们不应该一个接一个地设置状态,如果您有太多状态并且可能当一个状态可能影响另一个状态值时,不建议这样做,并且因为它是异步设置的,所以很有可能您最初设置的状态可能会在最后一个状态之后设置。对于这种情况,它还建议使用按顺序设置状态的功能 setState..
    • State 是按照您调用 setState 的顺序设置的。它是异步的,但尊重您调用它的顺序。当设置一个值取决于先前的值时,使用功能集状态,而不仅仅是任何先前的状态。
    • React 可以将多个 setState() 调用批处理到单个更新中以提高性能。因为 this.props 和 this.state 可能是异步更新的,所以你不应该依赖它们的值来计算下一个状态。您应该始终使用函数式方法进行这种操作,提供状态和道具并基于前者返回新状态。供参考:freecodecamp.org/news/…
    • 你所说的函​​数式 setState 在使用钩子时是无关紧要的,例如将函数传递给setCount,只允许访问之前的count 值。您所做的原始声明是“我们不应该一个接一个地设置状态,因为状态更新是异步的”。你真正的意思是“你不应该为一个值设置状态,然后根据第一次状态更改为另一个值设置状态,因为状态是异步更新的,所以第一次更改还没有发生。”连续多次调用setState 没有错。
    【解决方案2】:

    一个接一个地调用 set state 完全没问题,而且是正确的做法:

    const handleClick = () => {
      if (!isOnline) {
        setIsOnline(!isOnline)
        setCount(count + 1)
      } else {
        setIsOnline(!isOnline)
      }
    }
    

    状态是异步更新的,这意味着您的状态变量 isOnline 和 count 在您的组件重新渲染之前实际上不会改变。调用 setCount 和 setIsOnline 不会更新这些变量,而是告诉 React 在下一次渲染时更新。

    这就是为什么你不能这样做:

    const handleClick = () => {
        setCount(count + 1)
        setCount(count + 1)
    }
    

    计数将增加 1,而不是 2。

    为什么?

    因为值count 还没有更新,因为我们必须等到重新渲染才能更新它。这意味着count 在整个函数中具有相同的值——它永远不会改变。因此,您可以调用 setCount(count + 1) 一百万次,并且该值只会增加 1。

    这就是人们说你应该使用设置状态回调函数时的意思。

    这是设置状态回调函数:

    const handleClick = () => {
        setCount(prev => prev + 1)
        setCount(prev => prev + 1)
    }
    

    这按预期工作,count 现在将增加 2。

    如果我们传递一个像 prev =&gt; prev + 1 这样的函数来设置状态,React 会将最近的值传递给函数。

    规则是:

    每次使用旧状态值设置新状态值时 - 传递一个函数来设置状态。

    因此,尽管您当前的实现确实有效,但您确实应该传递一个函数来设置 count 上的状态,因为您依赖于先前的状态:

    const handleClick = () => {
      if (!isOnline) {
        setIsOnline(!isOnline)
        setCount(prev => prev + 1)
      } else {
        setIsOnline(!isOnline)
      }
    }
    

    通常你也应该为你的布尔值这样做,例如:

    setIsOnline(prev => !prev)
    

    但由于您在 if 语句中使用了 isOnline,因此您不应该在此处这样做,因为 prev 的值可能与您的 if 使用的值不同。

    【讨论】:

      猜你喜欢
      • 2021-02-08
      • 2021-02-21
      • 2020-05-18
      • 2020-01-29
      • 2020-11-30
      • 1970-01-01
      • 1970-01-01
      • 2021-11-27
      • 2020-11-10
      相关资源
      最近更新 更多