【问题标题】:ReactJS can't set state from an event with event.persist()ReactJS 无法使用 event.persist() 从事件中设置状态
【发布时间】:2017-06-24 16:39:22
【问题描述】:

我需要设置一个从事件中获得的状态字段,但是当我将函数传递给它时它没有被设置。组件和方法如下所示:

constructor(props: SomeProps, context: any) {
  super(props, context);
  this.state = {
    isFiltering: props.isFiltering,
    anchor: "",
  };
}

private toggleFilter = (event: any) => {
  event.persist()
  this.setState(prevState => ({
    isFiltering: !prevState.isFiltering,
    anchor: event.currentTarget // does not work, it's null
  }));
}

如果我删除 event.persist(),则会收到以下错误:

出于性能原因,会重复使用此合成事件。如果您看到这一点,则表示您正在访问方法 currentTarget 对已发布/无效的合成事件。这是一个无操作功能。如果您必须保留原始合成事件,请使用 event.persist()。请参阅https://facebook.github.io/react/docs/events.html#event-pooling 了解更多信息。

由于某种原因,以下代码有效:

private toggleFilter = (event: any) => {
  this.setState({anchor:event.currentTarget}) // works fine
  this.setState(prevState => ({
    isFiltering: !prevState.isFiltering,
  }));
}

为什么上述方法有效但我使用this.setState(prevState=> ...) 时无效?

【问题讨论】:

  • 我认为这是因为setState 是“异步”的,所以当你传递给setState 的函数被执行(并且事件被访问)时,事件已经不在了。在第二个版本中,立即访问事件并将其currentTarget 传递给setState
  • @thirtydot 谢谢,这是有道理的,但似乎event.persist() 是解决这个问题的合适方法,但它对我来说有点不起作用..
  • 是的。我不确定为什么它不起作用(没有自己测试),所以我没有写答案。
  • 应该没问题???
  • 您有什么特别的原因不想使用它吗? private toggleFilter = (event: any) => { this.setState({ isFiltering: !this.state.isFiltering, anchor: event.currentTarget }); }。可能更容易理解。

标签: javascript reactjs


【解决方案1】:

这是预期的行为,因为event.persist() 并不意味着currentTarget 没有被无效,事实上它应该是 - 这符合浏览器的本机实现。

这意味着如果您想以异步方式访问 currentTarget,您需要像在答案中那样将其缓存在一个变量中。


引用一位 React 核心开发人员 - Sophie Alpert

currentTarget 会随着事件的冒泡而发生变化——如果您在接收事件的元素上有一个事件处理程序,而在其祖先上有其他处理程序,他们会看到 currentTarget 的不同值。 IIRC 将其取消与本地事件发生的情况一致;如果没有,请告诉我,我们会在这里重新考虑我们的行为。

查看官方 React 存储库中讨论的 the source 以及 Sophie 提供的以下 sn-p,我已经接触了一点。

var savedEvent;
var savedTarget;

divb.addEventListener('click', function(e) {
  savedEvent = e;
  savedTarget = e.currentTarget;
  setTimeout(function() {
    console.log('b: currentTarget is now ' + e.currentTarget);
  }, 0);
}, false);

diva.addEventListener('click', function(e) {
  console.log('same event object? ' + (e === savedEvent));
  console.log('same target? ' + (savedTarget === e.currentTarget));
  setTimeout(function() {
    console.log('a: currentTarget is now ' + e.currentTarget);
  }, 0);
}, false);
div {
  padding: 50px;
  background: rgba(0,0,0,0.5);
  color: white;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>

  <div id="diva"><div id="divb"> Click me and see output! </div></div>
  
</body>
</html>

【讨论】:

    【解决方案2】:

    根据@thirtydot 的评论,我得到了一个可行的解决方案。

    我认为这是因为 setState 是“异步”的,所以当你传递给 setState 的函数被执行(并且事件被访问)时,事件不再存在。在第二个版本中,立即访问事件并将其 currentTarget 传递给 setState

    所以我将event.currentTarget 存储到一个变量中并使用它,正如ReactJs Event Pooling 中所解释的那样

    看起来像这样,它可以工作

    private toggleFilter = (event: any) => {
            let target = event.currentTarget;   
            this.setState((prevState) => ({
                isFiltering: !prevState.isFiltering,
                anchor: target
            }));
        }
    

    但这并不能解释为什么event.persist() 不起作用。我会接受解释它的答案。

    【讨论】:

      【解决方案3】:

      要解决这个问题 - 在调用表单的 handleSubmit 之前,调用 event.persist() 然后在 handleSubmit() 定义 - 写你的逻辑。例如

      <form id="add_exp_form" onSubmit={(event) => {event.persist();this.handleSubmit(event)}}>
      
      handleSubmit(event){
          // use event.currentTarget - It will be visible here
      }
      

      【讨论】:

        【解决方案4】:

        我在动态填充组件时遇到了类似的警告消息,为了解决这个问题,我只需要用箭头函数包装函数。

        发件人:

        { text: "Delete item", onClick: deleteItem },
        

        收件人:

        { text: "Delete item", onClick: (id) => deleteItem(id) },
        

        【讨论】:

          【解决方案5】:

          关于这个问题有一个很好的描述和 2 个不同的解决方案,你可以查看这个link

          【讨论】:

          • 感谢您指出此链接。我在设置状态之前选择了“缓存值”的解决方案,而不是 event.persist() :只需执行const value = event.target.value; setState(value)
          【解决方案6】:

          你可以像这样使用柯里化函数来处理它。

          const persistEvent = (fun: any) => (e: any) => {
          e.persist();
          fun(e);
          };
          

          现在你可以像这样编写函数了

          persistEvent(toggleFilter);
          

          会是这样的:

          const persistEvent = (fun: any) => (e: any) => {
              e.persist();
              fun(e);
              };
          
          const delayedFunctionality = (e) => {
            setTimeout(()=> {
               console.log(e.target.value)
            },500
          }
          
          <input onChange={persistEvent(delayedFunctionality)} />
          

          【讨论】:

            【解决方案7】:

            在你的函数中你应该使用 const event.preventDefault()event.prevent()

            示例:

            updateSpecs1 = (event) => { 
                event.preventDefault()
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-04-17
              • 1970-01-01
              • 1970-01-01
              • 2017-10-02
              • 2019-08-16
              • 2017-06-13
              • 2017-05-26
              相关资源
              最近更新 更多