反应状态变量在“ beforeunload”函数之前神秘地变化

时间:2020-04-05 19:07:42

标签: javascript reactjs

更新: 我在这里创建了一个最小的可复制样本: https://react-wsaei2.stackblitz.io 编辑器链接:https://stackblitz.com/edit/react-wsaei2

在我的应用中,当有人关闭页面而不自己做时,我在beforeunload中使用sendBeacon函数来解锁数据库文档。这是我在具有空数组的useEffect中执行的操作,因此它仅在启动时运行一次:

// Add event listener to run code before window closes
window.addEventListener("beforeunload", unlockStory);
return () => {
  window.removeEventListener("beforeunload", unlockStory);
}

这是unlockStory函数:

const unlockStory = (e) => {
    e.preventDefault();
    if (props.storyID && !success && !loggedOut) {
      console.log("Unlocking story. success, loggedOut", success, loggedOut);
      debugger;
      navigator.sendBeacon(`/api/stories/${props.storyID}/${loggedOut}/${success}`, JSON.stringify({body: {locked: true}}));
    }
    e.returnValue = "What's going on?";
  }

如您所见,我不希望每次都发送信标-仅当LoggedOut == false时(即,如果用户已经注销,我不想发送信标以解锁)。

问题是由于某种原因,即使事先已经正确,解锁日志功能loginOut始终为false!我在页面上放置了一个手动刷新按钮,以进行如下检查:

const handleRefresh = () => {
    console.log("Handling refresh: success, loggedOut", success, loggedOut);
    window.location.reload(false);
}

输出为:

Handling refresh: success, loggedOut false true
Unlocking story. success, loggedOut false false

为什么????

另一个奇怪的事情是,unlockStory函数中的debugger;行永远不会在正常刷新或页面关闭时触发,它仅在刷新时触发,这是由我进行文件更改导致的,因此npm自动刷新打开的页面。

请帮助,我很茫然,谢谢!

1 个答案:

答案 0 :(得分:1)

您需要将successloggedOut(以及其他使用的变量)定义为效果依赖项。

useEffect(() => {

  const unlockStory = (e) => {
    e.preventDefault();
    console.log("Inside unlockStory. success, loggedOut:", success, loggedOut);
    if (!success && !loggedOut) {
      console.log("Inside if")
      navigator.sendBeacon(`/api/stories/`, JSON.stringify({body: {locked: true}}));
    }
    e.returnValue = "What's going on?";
  }

  // Add event listener to run code before window closes
  window.addEventListener("beforeunload", unlockStory);
  return () => {
    window.removeEventListener("beforeunload", unlockStory);
  }

}, [success, loggedOut]); // Run on every success or loggedOut change

无需手动删除事件侦听器,如下所示:

React还会在下次运行效果之前清除上一个渲染中的效果。

有时,基于类的组件更易于使用:

class StartClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      loggedOut: false,
      success: false,
      storyObj: {
        segCount: 2
      }
    }
  }

  componentDidMount() {
    this.setState( { loggedOut: true } );
    window.addEventListener("beforeunload", this.unlockStory);
    return () => {
      window.removeEventListener("beforeunload", this.unlockStory);
    }
  }

  handleRefresh = () => {
    let {success, loggedOut} = this.state
    console.log("Handling refresh: success, loggedOut", success, loggedOut);
    window.location.reload(true);
  }

  unlockStory = (e) => {
    e.preventDefault();
    let {success, loggedOut} = this.state
    console.log("Inside unlockStory. success, loggedOut:", success, loggedOut);
    if (!success && !loggedOut) {
      console.log("Inside if")
      navigator.sendBeacon(`/api/stories/`, JSON.stringify({body: {locked: true}}));
    }
    e.returnValue = "What's going on?";
  }

  render() {
    return (
      <Container fluid>

        <Row>
          <Col>
          <p>
            To reproduce:<br/> Open console, then click the Refresh page button below. When the alert pops up check the console and you'll see that the loggedOut variable has changed from true to false.
          </p>
          <p>
            The same behavior occurs when you refresh via the browser, but you can't see the log as the console is cleared upon refresh.
          </p>
          {this.state.loggedOut && <Button onClick={this.handleRefresh}>Refresh page</Button>}
          </Col>
        </Row>


      </Container>
    );

  }
}