【问题标题】:React textarea callback when losing focus / onBlur失去焦点/ onBlur时反应textarea回调
【发布时间】:2020-09-02 13:34:57
【问题描述】:

https://codesandbox.io/s/react-textarea-callback-on-blur-yoh8n?file=/src/App.tsx

在 React 中有一个 textarea 我想实现两个基本用例:

  1. 当用户按下“Escape”时,移除焦点并重置某些状态
  2. 当用户在文本区域外点击并失去焦点时执行回调 (saveToDatabase) (=> onBlur)
<textarea
  ref={areaRef}
  value={input}
  onChange={handleChange}
  onKeyDown={handleKeyDown}
  onBlur={handleBlur}
/>

对于第一个用例,我在目标上调用blur()

const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
  if (e.key === "Escape") {
    console.log("Escape clicked");
    setInput(inputForReset);
    e.currentTarget.blur();
  }
};

..但这也调用了onBlur 处理程序,我实际上想将其用于第二个用例。我试图通过 ref 确定事件调用者是否是 textarea 本身,但这不起作用:

const handleBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
  console.log("blur");
  /**
   * Only save to database when losing focus through clicking
   * outside of the text area, not for every blur event.
   */
  if (areaRef.current && !areaRef.current.contains(e.currentTarget as Node)) {
    saveToDatabase();
  }
};

换句话说:当用户在 textarea 完成编辑时,我想将某些内容保存到数据库中,但我不知道如何区分我以编程方式触发的 blur 事件和原生 blur 事件当您在节点外部单击时 textarea 使用的。

【问题讨论】:

  • 你可以将它聚焦在其他任何地方,而不是调用模糊事件,可能在父级或任何地方
  • @Sim 我不这么认为,因为聚焦其他节点会在之前聚焦之前调用 textarea 的 onBlur 处理程序:codesandbox.io/s/…

标签: javascript reactjs typescript dom-events


【解决方案1】:

我注意到错误是什么。 blur 事件的目标是它自己

areaRef === event.target

所以你必须实现一个额外的功能来捕捉框外的点击。

import * as React from "react";
import "./styles.css";
import { useState, useRef, useEffect } from "react";

function useOutsideAlerter(
  ref: React.RefObject<HTMLTextAreaElement>,
  fun: () => void
) {
  useEffect(() => {
    function handleClickOutside(event: any) {
      if (
        ref.current &&
        !ref.current.contains(event.target) &&
        // THIS IS IMPORTANT TO CHECK
        document.activeElement === ref.current
      ) {
        fun();
      }
    }

    // Bind the event listener
    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      // Unbind the event listener on clean up
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [ref]);
}

export default function App() {
  const areaRef = useRef<HTMLTextAreaElement>(null);
  const [inputForReset, setInputForReset] = useState<string>("Original input");
  const [input, setInput] = useState<string>(inputForReset);

  const saveToDatabase = () => {
    console.log("save to database");
    setInputForReset(input);
    alert(input);
  };

  // OUT SIDE CLICK

  useOutsideAlerter(areaRef, saveToDatabase);

  const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setInput(e.target.value);
  };

  const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
    if (e.key === "Escape") {
      console.log("Escape clicked");
      setInput(inputForReset);
      e.currentTarget.blur();
      e.stopPropagation();
    }
  };

  // it doesnt work
  
  const handleBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
    /**
     * Only save to database when losing focus through clicking
     * outside of the text area, not for every blur event.
     */
    console.log(event.target);
    console.log(areaRef);

    if (areaRef.current && !areaRef.current.contains(event.target)) {
      saveToDatabase();
    }
  };

  return (
    <div className="App">
      <textarea
        ref={areaRef}
        value={input}
        onChange={handleChange}
        onKeyDown={handleKeyDown}
        onBlur={handleBlur}
        className="area"
      />
      <p>Input state: {input}</p>
    </div>
  );
}

请查看我的sandbox

【讨论】:

  • document.activeElement === ref.current 是缺失的部分!我已经有相同的实现来检查外部点击,但是当你随机点击其他地方时,我不知道如何不执行回调。
  • 对于未来的读者:这是完整版本:codesandbox.io/s/react-textarea-callback-on-blur-geo3w
【解决方案2】:

也许您可以添加一个变量(或一个类属性,如果您使用的是类组件而不是功能组件)作为“preventsaveOnBlur”标志;类似:

let preventSaveOnBlur: boolean = false;

const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
  if (e.key === "Escape") {
    console.log("Escape clicked");
    setInput(inputForReset);
    preventSaveOnBlur = true;  // will prevent saving on DB - see handleBlur 
    e.currentTarget.blur();
  }
};

const handleBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
  console.log("blur");
  /**
   * Only save to database when losing focus through clicking
   * outside of the text area, not for every blur event.
   */
  if (!preventSaveOnBlur) {
    saveToDatabase();
  }
};

const handleFocus = (e: React.FocusEvent<HTMLTextAreaElement>) => {
  preventSaveOnBlur = false;
};

在 textarea 中你也可以处理 onFocus 事件:

<textarea
  ref={areaRef}
  value={input}
  onChange={handleChange}
  onKeyDown={handleKeyDown}
  onBlur={handleBlur}
  onFocus={handleFocus} // will reset 'preventSaveOnBlur' to false every time the textarea get the focus
/> 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多