反应:仅在单击 html 元素时执行一次操作

React: Perform action, only once, when html element is clicked

本文关键字:操作 一次 执行 单击 元素 html 反应      更新时间:2023-09-26

在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 代码)?

使用 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>;
};

使用 refs。不是很美,但至少它有效。这需要 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>
}

翻转状态 元素触发其事件后,react 将重新渲染组件,因为您的状态发生了变化并且不会显示它。以下是实现该结果的一种方法示例。

_onEvent() {
    this.setState({
        hasTriggered: true
    })
}

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

函数式编程的高阶方法

裁判:

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

完整示例:

  • 答题器 0 将在每次点击时触发事件。
    • 这是有问题的。
    • 如果网络速度很慢并且用户再次单击希望数据提交得更快,这可能会导致服务器端的额外触发器。
  • Clicker 1 只会在按钮本身和点击函数 ref 重新渲染后再次触发。
    • 如果我们必须在服务器响应后重新渲染主组件,这是一个简单的解决方案。
  • 只有当 value2 有任何更改时,Locker 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;

在我看来,这是一个更简单的解决方案:

<div
  className={`tile ${tile.marked ? "team1" : ""}`}
  onClick={!tile.marked ? () => markTile(tile.id) : null}
></div>

这是 React 18 的。如您所见,我正在通过检查标记的属性是否为 false 来检查我是否已经单击了它。只有这样,我才能调用我的函数。