将具有状态的基于类的组件转换为带有钩子的功能组件

时间:2020-02-06 18:32:53

标签: javascript reactjs callback components react-hooks

我对此状态有状态,当用户在线时,它会增加计数。我想将其更改为带有钩子的功能组件,这已在下面完成

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,或者我在功能组件中带有钩子的实现是否正确??

2 个答案:

答案 0 :(得分: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状态异步问题的风险。

答案 1 :(得分:0)

一个接一个地调用设置状态是完全可以的,这是正确的做法:

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

状态会异步更新,这意味着状态变量isOnlinecount实际上不会更改,直到组件重新渲染为止。调用setCountsetIsOnline不会更新这些变量,但是会告诉React在下一个渲染器上进行更新。

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

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

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

为什么?

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

这就是人们说您应该使用set state回调函数的意思。

这是设置状态回调函数:

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

这可以按预期工作,并且count现在将增加2。

如果我们传递类似prev => prev + 1的函数来设置状态,React会将最新值传递给该函数。

规则是:

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

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

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

通常,您也应该为布尔值执行此操作,例如:

setIsOnline(prev => !prev)

但是由于您在if语句中使用isOnline,因此您不应该在此进行操作,因为prev的值可能与if使用的值不同。