为什么 setState of react-select's menu selection 使模态关闭状态?
Why setState of react-select's menu selection making modal close state?
我有一个模态框,它监听模态框的外部点击并触发关闭模态框的 onclose 方法。现在我将 react select 添加到模态,在选择其中一个选项后,它使我的模态进入关闭状态。
我正在从一篇媒体文章中捕获外部点击。
function useOuterClickNotifier(onOuterClick, innerRef) {
useEffect(() => {
if (innerRef.current) {
document.addEventListener("click", handleClick);
}
return () => document.removeEventListener("click", handleClick);
function handleClick(e) {
if (innerRef.current && !innerRef.current.contains(e.target)) {
onOuterClick(e);
}
}
}, [onOuterClick, innerRef]);
}
我的index.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import Select from "react-select";
import Modal from "../Modal";
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" }
];
function App() {
const [isOpen, setOpen] = useState(false);
function onCloseModal() {
console.log("Why closing?");
setOpen(false);
}
function openModal() {
setOpen(true);
}
return (
<>
{isOpen && (
<Modal closeModal={onCloseModal}>
<div className="card">
<Select options={options} />
</div>
</Modal>
)}
<button onClick={openModal}>Open modal</button>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
我该如何解决?
https://codesandbox.io/s/usegooglemap-repro-x3q37?fontsize=14&hidenavigation=1&theme=dark
系统信息
System:
OS: macOS 10.14.5
CPU: (12) x64 Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
Binaries:
Node: 12.4.0 - ~/.nvm/versions/node/v12.4.0/bin/node
Yarn: 1.16.0 - ~/.nvm/versions/node/v12.4.0/bin/yarn
npm: 6.9.0 - ~/.nvm/versions/node/v12.4.0/bin/npm
Browsers:
Chrome: 78.0.3904.97
Firefox: 69.0
Safari: 12.1.1
npmPackages:
react: 16.11.0 => 16.11.0
react-dom: 16.11.0 => 16.11.0
react-scripts: 3.2.0 => 3.2.0
我认为这是由于调用您的钩子的事件侦听器时select菜单已经卸载造成的。 React 不知道手动附加的点击处理程序,因此它将立即开始更新 DOM。当您的侦听器处理该事件时,单击的 DOM 节点已经被删除。
为了防止这种情况,您可以将 true
作为第三个参数传递给 addEventListener
和 removeEventListener
。这个参数是useCapture
。如果 true
它将在捕获阶段 在 目标元素的任何侦听器之前调用您的侦听器。
useCapture
[Optional]
A Boolean indicating whether events of this type will be dispatched to
the registered listener
before being dispatched to any EventTarget
beneath it in the DOM tree. Events that are bubbling upward through
the tree will not trigger a listener designated to use capture. Event
bubbling and capturing are two ways of propagating events which occur
in an element that is nested within another element, when both
elements have registered a handle for that event. The event
propagation mode determines the order in which elements receive the
event. See DOM Level 3 Events and JavaScript Event order for a
detailed explanation. If not specified, useCapture
defaults to false
.
function useOuterClickNotifier(onOuterClick, innerRef) {
useEffect(() => {
if (innerRef.current) {
document.addEventListener("click", handleClick, true);
}
return () => document.removeEventListener("click", handleClick, true);
function handleClick(e) {
if (innerRef.current && !innerRef.current.contains(e.target)) {
onOuterClick(e);
}
}
}, [onOuterClick, innerRef]);
}
我有一个模态框,它监听模态框的外部点击并触发关闭模态框的 onclose 方法。现在我将 react select 添加到模态,在选择其中一个选项后,它使我的模态进入关闭状态。 我正在从一篇媒体文章中捕获外部点击。
function useOuterClickNotifier(onOuterClick, innerRef) {
useEffect(() => {
if (innerRef.current) {
document.addEventListener("click", handleClick);
}
return () => document.removeEventListener("click", handleClick);
function handleClick(e) {
if (innerRef.current && !innerRef.current.contains(e.target)) {
onOuterClick(e);
}
}
}, [onOuterClick, innerRef]);
}
我的index.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import Select from "react-select";
import Modal from "../Modal";
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" }
];
function App() {
const [isOpen, setOpen] = useState(false);
function onCloseModal() {
console.log("Why closing?");
setOpen(false);
}
function openModal() {
setOpen(true);
}
return (
<>
{isOpen && (
<Modal closeModal={onCloseModal}>
<div className="card">
<Select options={options} />
</div>
</Modal>
)}
<button onClick={openModal}>Open modal</button>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
我该如何解决?
https://codesandbox.io/s/usegooglemap-repro-x3q37?fontsize=14&hidenavigation=1&theme=dark
系统信息
System:
OS: macOS 10.14.5
CPU: (12) x64 Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
Binaries:
Node: 12.4.0 - ~/.nvm/versions/node/v12.4.0/bin/node
Yarn: 1.16.0 - ~/.nvm/versions/node/v12.4.0/bin/yarn
npm: 6.9.0 - ~/.nvm/versions/node/v12.4.0/bin/npm
Browsers:
Chrome: 78.0.3904.97
Firefox: 69.0
Safari: 12.1.1
npmPackages:
react: 16.11.0 => 16.11.0
react-dom: 16.11.0 => 16.11.0
react-scripts: 3.2.0 => 3.2.0
我认为这是由于调用您的钩子的事件侦听器时select菜单已经卸载造成的。 React 不知道手动附加的点击处理程序,因此它将立即开始更新 DOM。当您的侦听器处理该事件时,单击的 DOM 节点已经被删除。
为了防止这种情况,您可以将 true
作为第三个参数传递给 addEventListener
和 removeEventListener
。这个参数是useCapture
。如果 true
它将在捕获阶段 在 目标元素的任何侦听器之前调用您的侦听器。
useCapture
[Optional]A Boolean indicating whether events of this type will be dispatched to the registered
listener
before being dispatched to anyEventTarget
beneath it in the DOM tree. Events that are bubbling upward through the tree will not trigger a listener designated to use capture. Event bubbling and capturing are two ways of propagating events which occur in an element that is nested within another element, when both elements have registered a handle for that event. The event propagation mode determines the order in which elements receive the event. See DOM Level 3 Events and JavaScript Event order for a detailed explanation. If not specified,useCapture
defaults tofalse
.
function useOuterClickNotifier(onOuterClick, innerRef) {
useEffect(() => {
if (innerRef.current) {
document.addEventListener("click", handleClick, true);
}
return () => document.removeEventListener("click", handleClick, true);
function handleClick(e) {
if (innerRef.current && !innerRef.current.contains(e.target)) {
onOuterClick(e);
}
}
}, [onOuterClick, innerRef]);
}