React前端技巧

点击 React组件 以外的其他地方执行相应操作

2021-01-06  本文已影响0人  Lia代码猪崽

场景分析

比如说是有个按钮 <button/> ,点击这个按钮会出现下拉框 <Select /> ,想点击除了下拉框其他的地方能关闭下拉框。

技术拆解

1. 先写好只能由按钮控制显示隐藏

import React, { useState } from "react";

function Select({ onSelect }) {
  const data = ["春", "夏", "秋", "东"];
  return (
    <ul
      style={{
        background: "#fff",
        color: "#333",
        width: "200px",
        border: "1px solid #999",
        position: "absolute",
        left: "35px",
        top: "15px",
      }}
    >
      {data.map((item) => (
        <li key={item} onClick={() => onSelect(item)}>
          {item}
        </li>
      ))}
    </ul>
  );
}

function App() {
  const [showSelect, setShowSelect] = useState(false);
  const [season, setSeason] = useState("");

  const clickButton = () => {
    setShowSelect(!showSelect);
  };
  const onSelect = (item) => {
    setSeason(item);
  };

  return (
    <div className="App">
      <div style={{ position: "relative", width: "300px" }}>
        <button onClick={clickButton}>选择现在的季节:</button>
        {season}
        {showSelect && <Select onSelect={onSelect} />}
      </div>
    </div>
  );
}

export default App;
页面丑请忽略样式
点击按钮后会出现选择框,但只能通过再点一次按钮才能关闭

2. 通过给 document 增加点击事件监听来做到点其他地方关闭下拉框

这里要特别注意,<button/><Select /> 的区域也是在 document 里的,但是如果点击就隐藏下拉框这不是我们想要的结果,所以还需要在监听方法回调里做判断。

  1. 使用 useRef 获取到结点数据 。
import React, { useState, useRef } from "react";

// ...
function App() {
  // ...
  const selectRef = useRef(null);
  // ...
  return (
    <div className="App">
      <div style={{ position: "relative", width: "300px" }} ref={selectRef}>
       // ...
  );  
}

  1. 使用 useEffect 来给 document 增加点击事件监听。
import React, { useState, useRef, useEffect } from "react";

//...
function App() {
  // ...

  useEffect(() => {
    if (showSelect) {
      document.addEventListener("click", clickCallback, false);
      return () => {
        document.removeEventListener("click", clickCallback, false);
      };
    }
  }, [showSelect]);

  // ...
}
  1. 编写 clickCallback 回调函数。
import React, { useState, useRef, useEffect } from "react";

//...
function App() {
  // ...

  function clickCallback(event) {
    if (selectRef.current.contains(event.target)) {
      return;
    }
    setShowSelect(false);
  }

  // ...
}
  1. 完美,这下点了除 selectRef 以外的地方都会隐藏 <Select /> 了。

完整代码

import React, { useState, useRef, useEffect } from "react";

function Select({ onSelect }) {
  const data = ["春", "夏", "秋", "东"];
  return (
    <ul
      style={{
        background: "#fff",
        color: "#333",
        width: "200px",
        border: "1px solid #999",
        position: "absolute",
        left: "35px",
        top: "15px",
      }}
    >
      {data.map((item) => (
        <li key={item} onClick={() => onSelect(item)}>
          {item}
        </li>
      ))}
    </ul>
  );
}

function App() {
  const [showSelect, setShowSelect] = useState(false);
  const [season, setSeason] = useState("");
  const selectRef = useRef(null);

  const clickButton = () => {
    setShowSelect(!showSelect);
  };
  const onSelect = (item) => {
    setSeason(item);
  };

  function clickCallback(event) {
    if (selectRef.current.contains(event.target)) {
      return;
    }
    setShowSelect(false);
  }

  useEffect(() => {
    if (showSelect) {
      document.addEventListener("click", clickCallback, false);
      return () => {
        document.removeEventListener("click", clickCallback, false);
      };
    }
  }, [showSelect]);

  return (
    <div className="App">
      <div style={{ position: "relative", width: "300px" }} ref={selectRef}>
        <button onClick={clickButton}>选择现在的季节:</button>
        {season}
        {showSelect && <Select onSelect={onSelect} />}
      </div>
    </div>
  );
}

export default App;

上一篇下一篇

猜你喜欢

热点阅读