Reactでコンポーネントの外側をクリック検知する - useClickAwayListener
React コンポーネントの外側をクリック検出する
概要
useRef と useEffect フック
この方法は、useRef
フックを使用して、コンポーネントの外側をクリックするための参照を作成し、useEffect
フックを使用して、その参照がクリックされたかどうかを監視します。
const ref = useRef(null);
useEffect(() => {
const handleClickOutside = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
// 外側がクリックされた時の処理
}
};
document.addEventListener("click", handleClickOutside);
return () => {
document.removeEventListener("click", handleClickOutside);
};
}, [ref]);
useClickAwayListener フック
material-ui
ライブラリでは、useClickAwayListener
フックという便利なフックが提供されています。 このフックを使用すると、コンポーネントの外側がクリックされたかどうかを簡単に検出できます。
import { useClickAwayListener } from "@material-ui/core";
const MyComponent = () => {
const [open, setOpen] = useState(false);
const handleClickAway = () => {
setOpen(false);
};
const { ref } = useClickAwayListener({ onClickAway: handleClickAway });
return (
<div ref={ref}>
{open && <MyDropdown />}
</div>
);
};
ポータルを使用すると、コンポーネントを DOM ツリー内の別の場所にレンダリングできます。 これにより、コンポーネントの外側をクリック検出する際に、DOM 構造の問題を回避することができます。
import { createPortal } from "react-dom";
const MyComponent = () => {
const [open, setOpen] = useState(false);
const handleClickOutside = (event) => {
if (!portalRef.current.contains(event.target)) {
setOpen(false);
}
};
const portalRef = useRef(null);
useEffect(() => {
const portal = createPortal(<MyDropdown />, portalRef.current);
document.body.appendChild(portal);
return () => {
document.body.removeChild(portal);
};
}, [open]);
return (
<div>
{open && <div ref={portalRef} />}
</div>
);
};
これらの方法のどれを使用するかは、状況によって異なります。 シンプルな方法としては、useRef
と useEffect
フックを使用する方法がおすすめです。 より複雑な場合や、material-ui
ライブラリを使用している場合は、useClickAwayListener
フックを使用するのが良いでしょう。 ポータルを使用する方法は、より高度な方法ですが、DOM 構造の問題を回避することができます。
補足
- 上記のコードはあくまで例であり、実際のコードは状況に合わせて調整する必要があります。
useRef と useEffect フック
function App() {
const ref = useRef(null);
useEffect(() => {
const handleClickOutside = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
console.log("外側がクリックされました");
}
};
document.addEventListener("click", handleClickOutside);
return () => {
document.removeEventListener("click", handleClickOutside);
};
}, [ref]);
return (
<div ref={ref}>
<h1>コンポーネント内</h1>
<p>この部分をクリックしても、外側がクリックされたとは判定されません。</p>
</div>
);
}
useClickAwayListener フック
import { useClickAwayListener } from "@material-ui/core";
function App() {
const [open, setOpen] = useState(false);
const handleClickAway = () => {
setOpen(false);
};
const { ref } = useClickAwayListener({ onClickAway: handleClickAway });
return (
<div>
<button onClick={() => setOpen(true)}>開く</button>
{open && (
<div ref={ref}>
<h1>ドロップダウン</h1>
<p>この部分をクリックしても、外側がクリックされたとは判定されません。</p>
</div>
)}
</div>
);
}
ポータル
import { createPortal } from "react-dom";
function App() {
const [open, setOpen] = useState(false);
const handleClickOutside = (event) => {
if (!portalRef.current.contains(event.target)) {
setOpen(false);
}
};
const portalRef = useRef(null);
useEffect(() => {
const portal = createPortal(<MyDropdown />, portalRef.current);
document.body.appendChild(portal);
return () => {
document.body.removeChild(portal);
};
}, [open]);
return (
<div>
<button onClick={() => setOpen(true)}>開く</button>
{open && <div ref={portalRef} />}
</div>
);
}
function MyDropdown() {
return (
<div>
<h1>ドロップダウン</h1>
<p>この部分をクリックしても、外側がクリックされたとは判定されません。</p>
</div>
);
}
React コンポーネントの外側をクリック検出する他の方法
ReactDOM.findDOMNode
を使用して、コンポーネントの DOM ノードを取得し、そのノードに click
イベントリスナーを追加することができます。
const node = ReactDOM.findDOMNode(this);
node.addEventListener("click", (event) => {
if (event.target !== node) {
// 外側がクリックされた時の処理
}
});
Higher-Order Component (HOC)
HOC を使用して、コンポーネントの外側をクリック検出する機能を他のコンポーネントに簡単に追加することができます。
const withClickOutside = (Component) => {
return class extends React.Component {
constructor(props) {
super(props);
this.ref = React.createRef();
}
componentDidMount() {
document.addEventListener("click", this.handleClickOutside);
}
componentWillUnmount() {
document.removeEventListener("click", this.handleClickOutside);
}
handleClickOutside = (event) => {
if (this.ref.current && !this.ref.current.contains(event.target)) {
// 外側がクリックされた時の処理
}
};
render() {
return <Component ref={this.ref} {...this.props} />;
}
};
};
const MyComponent = () => {
return (
<div>
<h1>コンポーネント内</h1>
<p>この部分をクリックしても、外側がクリックされたとは判定されません。</p>
</div>
);
};
const MyComponentWithClickOutside = withClickOutside(MyComponent);
ライブラリの使用
react-onclickoutside
などのライブラリを使用して、コンポーネントの外側をクリック検出することができます。
import React from "react";
import onClickOutside from "react-onclickoutside";
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.handleClickOutside = this.handleClickOutside.bind(this);
}
handleClickOutside() {
// 外側がクリックされた時の処理
}
render() {
return (
<div ref={this.props.ref} onClickOutside={this.handleClickOutside}>
<h1>コンポーネント内</h1>
<p>この部分をクリックしても、外側がクリックされたとは判定されません。</p>
</div>
);
}
}
export default onClickOutside(MyComponent);
これらの方法のどれを使用するかは、状況によって異なります。 シンプルな方法としては、useRef
と useEffect
フックを使用する方法がおすすめです。 より複雑な場合や、ライブラリを使用したい場合は、他の方法を検討することができます。
javascript dom reactjs