为什么React Hook中的useState无法更新状态

时间:2019-07-09 14:11:48

标签: reactjs react-hooks

当我尝试使用React Hook中的示例时,我遇到关于useState的问题。

在下面的代码中,当单击按钮时,我添加了document的事件并检查了count的值。 我的期望是在count中获得console.log并以相同的方式查看。但是实际上,我在console中获得了旧值(初始值),并在view中获得了新值。我无法理解为什么视图中的count和事件回调中的count没有变化。

还有一件事情,当我使用setCount(10);(固定数字)时。然后多次单击按钮(> 2),然后在外部单击,我从checkCount仅得到2条日志。是React手表count不变还是下次addEventListener不变。

import React, { useState } from "react";

function Example() {
  const [count, setCount] = useState(0);

  const add = () => {
    setCount(count + 1);
    console.log("value set is ", count);
    document.addEventListener("click", checkCount);
  };

  const checkCount = () => {
    console.log(count);
  };

  return (
    <div>
      <p>You clicked {count} times</p>
      <p>Click button first then click outside button and see console</p>
      <button onClick={() => add()}>Click me</button>
    </div>
  );
}

export default Example;

2 个答案:

答案 0 :(得分:0)

如果要使用document.addEventListener捕获组件外部的事件,则需要使用useEffect钩子添加事件,然后可以使用useState确定是否捕获是否。

useEffect中传递[capture]的通知,这将使它发生变化,以便useEffect会在此更改时被调用,对此捕获布尔值的简单检查将确定是否添加事件

通过使用useEffect,我们还避免了任何内存泄漏,这也可以解决在卸载组件时也知道要删除事件的情况。

const {useState, useEffect} = React;


function Test() {
  const [capture, setCapture] = useState(false);
  const [clickInfo, setClickInfo] = useState("Not yet");
  
  function outsideClick() {
    setClickInfo(Date.now().toString());
  }
  
  useEffect(() => {
    if (capture) {
      document.addEventListener("click", outsideClick);
      return () => {
        document.removeEventListener("click", outsideClick);
      }
    }
  }, [capture]);
  
  return <div>
    <p>
    Click start capture, then click anywhere, and then click stop capture, and click anywhere.</p>
    <p>{capture ? "Capturing" : "Not capturing"}</p>
    <p>Clicked outside: {clickInfo}</p>
    <button onClick={() => setCapture(true)}>
      Start Capture
     </button>
    <button onClick={() => setCapture(false)}>
      Stop Capture
    </button>
  </div>
}

ReactDOM.render(<React.Fragment>
  <Test/>
</React.Fragment>, document.querySelector('#mount'));
p { user-select: none }
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="mount"></div>

答案 1 :(得分:0)

@Keith我理解您的示例,但是当我申请时会感到困惑。最初,我总是将函数调用为handleClick,并且在运行handleClickOutside之后仍然调用它,但是现在我不知道如何使用钩子将其应用。

这是我要插入的Hook代码

class ClickOutSide extends Component {
  constructor(props) {
    super(props)
    this.wrapperRef = React.createRef();
    this.state = {
      active: false
    }
  }

  handleClick = () => {
    if(!this.state.active) {
      document.addEventListener("click", this.handleClickOut);
      document.addEventListener("contextmenu", this.handleClickOut);
      this.props.clickInside();
    } else {
      document.removeEventListener("click", this.handleClickOut);
      document.removeEventListener("contextmenu", this.handleClickOut);

    }
    this.setState(prevState => ({
      active: !prevState.active,
   }));
  };

  handleClickOut = event => {
    const { target } = event;
    if (!this.wrapperRef.current.contains(target)) {
      this.props.clickOutside();
    }

    this.handleClick()
  }

  render(){
    return (
      <div
        onDoubleClick={this.props.onDoubleClick}
        onContextMenu={this.handleClick}
        onClick={this.handleClick}
        ref={this.wrapperRef}
      >
        {this.props.children}
      </div>
    )
  }
}

export default ClickOutSide