【问题标题】:React: Perform action, only once, when html element is clickedReact:单击html元素时仅执行一次操作
【发布时间】:2016-06-17 10:12:20
【问题描述】:

在 jQuery 中,当html 元素上触发事件时执行一次操作非常简单。

例如:

$(".dropdown-trigger").on("click", function(event) {
  var self = this;
  $("html").one("click", function() {
    $(self).parents(".dropdown").removeClass("active");
  });
  $(this).parents(".dropdown").toggleClass("active");

  event.stopPropagation();
});

在 React 中,这并不容易。

如何在 React 中执行一个操作,当单击 html 元素时,只执行一次该操作(类似于我的 jQuery 代码)?

【问题讨论】:

  • @JCOC611 再次阅读问题; OP 正在询问如何设置一个处理程序,该处理程序在被触发一次后自动解除绑定。
  • OP,您可以使用状态标志来执行此操作,但您的 jQuery 示例代码表明这是一个 XY 问题。我永远不会使用.one 来执行清除下拉菜单之类的操作。
  • @Mathletics 谢谢。你能给我一个例子吗?我很乐意为您提供答案。
  • 再次,这是an XY Problem。请告诉我们您要解决的问题以及您希望实现的目标。您在问如何实施特定(IMO 可能无效)的解决方案。

标签: javascript reactjs


【解决方案1】:

使用参考。不是很漂亮,但至少它有效。这需要 React >16.8

import React, { useEffect, useRef } from 'react'

const App = () => {
  const ref = useRef()

  // Wait for DOM to load
  useEffect(() => ref.addEventListener('click', () => alert('Hi'), { once: true })

  return <button ref={ref}>Click on me (once)</button>
}

【讨论】:

  • 对我帮助很大!谢谢你拯救了我的一天! :)
【解决方案2】:

使用 useState,也适用于 React > 16.8

import React, { useState } from 'react';

const App = () => {
  const [clicked, setClicked] = useState(false);
  const myFunc = () => alert('hey');
  const onClick = (event) => {
    if (!clicked) {
      setClicked(true);
      myFunc(event);
    }
  };
  return <button onClick={onClick}>Click on me (once)</button>;
};

【讨论】:

    【解决方案3】:

    一旦元素触发了它的事件,就翻转状态,react 将重新渲染组件,因为你的状态改变了,它不会被显示。下面是实现该结果的一种方法的示例。

    _onEvent() {
        this.setState({
            hasTriggered: true
        })
    }
    
    
    render() {
        return (
            <div>
                {
                    !this.state.hasTriggered ?
                    <MyElement onClick={this._onEvent.bind(this)} /> : null
                }
            </div>
        )
    }
    

    【讨论】:

      【解决方案4】:

      函数式编程的高阶方法

      参考:

      https://betterprogramming.pub/how-to-allow-only-one-click-events-in-javascript-72938027fbf5
      

      要点:

      const once = (f) => {
        let finished = false;
        return (...args) => {
          if (!finished) {
            finished = true;
            f(...args);
          }
        };
      };
      

      完整示例:

      • clicker 0 将在每次点击时触发事件。
        • 这是有问题的。
        • 如果网络速度较慢并且用户再次单击希望数据能够更快提交,这可能会导致服务器端的额外触发器。
      • clicker 1 只会在按钮本身和 click 函数 ref 重新渲染后再次触发。
        • 如果我们必须在服务器响应后重新渲染主要组件,这是一个简单的解决方案。
      • 只有当 value2 有任何变化时,clicker 2 才会再次触发。
        • 如果我们想使用特定状态进行控制
      import "./App.css";
      import { useState, useCallback } from "react";
      
      const once = (f) => {
        let finished = false;
        return (...args) => {
          if (!finished) {
            finished = true;
            f(...args);
          }
        };
      };
      
      function App() {
        const [value1, setValue1] = useState("value1");
        const [value2, setValue2] = useState("value2");
      
        console.log(`app rendered`);
      
        const onChange1 = useCallback((e) => {
          console.log(`onChange1`, e.target.value);
          setValue1(e.target.value);
        }, []);
      
        const onChange2 = useCallback((e) => {
          console.log(`onChange2`, e.target.value);
          setValue2(e.target.value);
        }, []);
      
        const onClick0 = () => {
          // mocking 2 secs network request
          setTimeout(() => {
            // set value to value1 to cause the re-render
            setValue1(new Date());
            console.log("clicker 0");
          }, 2000);
        };
      
        const onClick1 = () => {
          // mocking 2 secs network request
          setTimeout(() => {
            // set value to value1 to cause the re-render
            setValue1(new Date());
            console.log("clicker 1");
          }, 2000);
        };
      
        const onClick2 = () => {
          // mocking 2 secs network request
          setTimeout(() => {
            // set value to value1 to cause the re-render
            setValue1(new Date());
            console.log("clicker 2");
          }, 2000);
        };
      
        const memoOnceOnClick2 = useCallback(once(onClick2), [value2]);
      
        return (
          <div className="App">
            <header className="App-header">
              <input value={value1} onChange={onChange1} />
              <input value={value2} onChange={onChange2} />
              <button onClick={onClick0}>
                clicker 0 / run every time i am clicked
              </button>
              <button onClick={once(onClick1)}>
                clicker 1 / run once until i am re-render
              </button>
              <button onClick={memoOnceOnClick2}>
                clicker 2 / run once until value2 is changed
              </button>
            </header>
          </div>
        );
      }
      
      export default App;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-28
        • 2016-03-26
        相关资源
        最近更新 更多