Javascript 在'之前对神秘变化的状态变量作出反应;卸载前';功能

Javascript 在'之前对神秘变化的状态变量作出反应;卸载前';功能,javascript,reactjs,Javascript,Reactjs,更新: 我在这里创建了一个最小可复制样本: 编辑链接: 在我的应用程序中,我使用beforeunload中的sendBeacon函数来解锁数据库文档,当有人关闭页面而自己不这样做时。下面是我在使用空数组的useEffect中如何做到这一点的,这样它在启动时只运行一次: // Add event listener to run code before window closes window.addEventListener("beforeunload", unlockStory); return

更新: 我在这里创建了一个最小可复制样本: 编辑链接:

在我的应用程序中,我使用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时才发送(即,如果用户已注销,我不希望发送信标解锁)

问题在于,由于某种原因,unlockStory函数loggedOut始终为false,即使它之前为true!我在页面上放置了一个手动刷新按钮以进行如下检查:

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
为什么

另一件奇怪的事情是
调试器行在正常刷新或页面关闭时不会被触发,它仅在我更改文件导致刷新时被触发,因此npm会自动刷新打开的页面


请帮忙,我不知所措,谢谢

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

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>
    );

  }
}
类StartClass扩展了React.Component{
建造师(道具){
超级(道具);
此.state={
洛格多特:错,
成功:错,
故事对象:{
分段数:2
}
}
}
componentDidMount(){
this.setState({loggedOut:true});
addEventListener(“beforeunload”,this.unlockStory);
return()=>{
removeEventListener(“beforeunload”,this.unlockStory);
}
}
handleRefresh=()=>{
让{success,loggedOut}=this.state
log(“处理刷新:成功,loggedOut”,成功,loggedOut);
window.location.reload(true);
}
解锁故事=(e)=>{
e、 预防默认值();
让{success,loggedOut}=this.state
log(“insideunlockstory.success,loggedOut:”,success,loggedOut);
如果(!success&&!loggedOut){
console.log(“内部if”)
sendBeacon(`/api/stories/`,JSON.stringify({body:{locked:true}}));
}
e、 returnValue=“发生了什么事?”;
}
render(){
返回(

要复制:
打开控制台,然后单击下面的刷新页面按钮。当警报弹出时,检查控制台,您将看到loggedOut变量已从true更改为false。

当您通过浏览器刷新时,也会发生相同的行为,但您无法看到日志,因为控制台在刷新时被清除。

{this.state.loggedOut&&Refresh page} ); } }
您需要将
success
loggedOut
(以及其他使用的变量)定义为效果依赖项

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>
    );

  }
}
类StartClass扩展了React.Component{
建造师(道具){
超级(道具);
此.state={
洛格多特:错,
成功:错,
故事对象:{
分段数:2
}
}
}
componentDidMount(){
this.setState({loggedOut:true});
addEventListener(“beforeunload”,this.unlockStory);
return()=>{
removeEventListener(“beforeunload”,this.unlockStory);
}
}
handleRefresh=()=>{
让{success,loggedOut}=this.state
log(“处理刷新:成功,loggedOut”,成功,loggedOut);
window.location.reload(true);
}
解锁故事=(e)=>{
e、 预防默认值();
让{success,loggedOut}=this.state
log(“insideunlockstory.success,loggedOut:”,success,loggedOut);
如果(!success&&!loggedOut){
console.log(“内部if”)
sendBeacon(`/api/stories/`,JSON.stringify({body:{locked:true}}));
}
e、 returnValue=“发生了什么事?”;
}
render(){
返回(

要复制:
打开控制台,然后单击下面的刷新页面按钮。当警报弹出时,检查控制台,您将看到loggedOut变量已从true更改为false。

当您通过浏览器刷新时,也会发生相同的行为,但您无法看到日志,因为控制台在刷新时被清除。

{this.state.loggedOut&&Refresh page} ); } }
首先,您没有删除侦听器,因为您正在实例化一个新函数。您必须传递要删除的侦听器的引用。将
unlockStory
放在相同的
useEffect
@xadm中。我这样做了,没有任何区别。问题仍然是,由于某种原因,这些变量正在发生变化success和loggedOut都是由setSuccess和setLoggedOut设置的React状态变量。您可以在输出中看到,loggedOut在重新加载时从true变为false,这非常令人困惑。同样让人困惑的是
调试器仅在每次发送信标时偶尔发射。准备使用codesandbox或stackblitz@xadm以前从未这样做过,我在stackblitz上创建了一个:不确定是否需要编辑器链接;让我知道。首先,您没有删除侦听器,因为您正在实例化一个新函数。您必须传递要删除的侦听器的引用。将
unlockStory
放在同一