I am trying when I click outside the div to close the div. Here is my code :
import { useRef, useState } from "react";
import "./styles.css";
const App = () => {
const [showModal, setShowModal] = useState(false);
const noderef = useRef(null);
const handleClick = () => {
console.log(showModal);
if (!showModal) {
console.log("test1");
document.addEventListener("click", handleOutsideClick, true);
} else {
console.log("test2");
document.removeEventListener("click", handleOutsideClick, true);
}
setShowModal(!showModal);
};
const handleOutsideClick = (e) => {
if (!noderef.current.contains(e.target)) handleClick();
};
return (
<div ref={noderef}>
<button onClick={() => handleClick()}>Open Modal</button>
{showModal ? (
<div className="modal">
I'm a modal!
<button onClick={() => handleClick()}>close modal</button>
</div>
) : null}
</div>
);
};
export default App;
When I click on Open Modal it works also when I click on close modal. But when I click outside the div the div is still opened.
How can I do to fix that, it looks it is like there are two clicks
Here is the full project : My project
Related
I am trying to use a popconfirm of ant design to confirm the closing of a modal, the following code I get the popconfirm to fire and the modal to close, but the popconfirm stays open.
does anyone have any idea how to make the popconfirm close when it is confirmed by giving it yes ?
import { Modal, Popconfirm } from 'antd';
import { useState } from 'react';
export const Ensayo=()=> {
const [open, setOpen] = useState(false);
const [popupOpen, setPopupOpen] = useState(false);
const showModal = () => {
setOpen(true);
};
const handleCancel = () => {
setPopupOpen(true);
};
const handlePopupConfirm = () => {
setOpen(false);
setPopupOpen(false);
};
const handlePopupCancel = () => {
setPopupOpen(false);
};
return (
<div>
<button type="button" onClick={showModal}>
Open Modal
</button>
<Modal
title="Basic Modal"
open={open}
onCancel={handleCancel}
>
<p>Some contents...</p>
<Popconfirm
title="Are you sure you want to close this modal?"
open={popupOpen}
onConfirm={handlePopupConfirm}
onCancel={handlePopupCancel}
okText="Yes"
cancelText="No"
/>
</Modal>
</div>
);
}
I have tried to add the popupOpen condition as a condition for the popconfirm rendering but it doesn't work with that condition it never triggers the popconfirm.
You can move the <Popconfirm> component outside the <Modal>
check the following example
import { Modal, Popconfirm } from "antd";
import { useState } from "react";
export const Ensayo = () => {
const [open, setOpen] = useState(false);
const [popupOpen, setPopupOpen] = useState(false);
const showModal = () => {
setOpen(true);
};
const handleCancel = () => {
setPopupOpen(true);
};
const handlePopupConfirm = () => {
setOpen(false);
setPopupOpen(false);
};
const handlePopupCancel = () => {
setPopupOpen(false);
};
return (
<div>
/* Popconfirm moved here */
<Popconfirm
title="Are you sure you want to close this modal?"
open={popupOpen}
onConfirm={handlePopupConfirm}
onCancel={handlePopupCancel}
okText="Yes"
cancelText="No"
/>
<button type="button" onClick={showModal}>
Open Modal
</button>
<Modal title="Basic Modal" open={open} onCancel={handleCancel}>
<p>Some contents...</p>
</Modal>
</div>
);
};
export default Ensayo;
Output:
I am finishing up a project where I want to use a small dropdown menu when I click on my settings icon. The problem is that for some reason it is not recognized when I click outside of that dropdown menu. I used a hook that worked in the same project with a different dropdown menu, but now it doesn't. Maybe because it is in a modal? I really don't know.
Here is the Repo of this Project: https://github.com/Clytax/fem-kanban
The Hook (I modified it a bit by excluding the elipsis icon so it doesnt reopen when Click on it to close it.)
import React from "react";
export const useOutsideClick = (callback, exclude) => {
const ref = React.useRef();
React.useEffect(() => {
const handleClick = (e) => {
if (
ref.current &&
!ref.current.contains(e.target) &&
!exclude.current.contains(e.target)
) {
callback();
}
};
document.addEventListener("click", handleClick);
return () => {
document.removeEventListener("click", handleClick);
};
}, [callback, exclude]);
return ref;
};
The Modal:
import React, { useRef, useState, useEffect } from "react";
import "./taskModal.scss";
import { ReactComponent as Elipsis } from "../../../assets/Icons/icon-vertical-ellipsis.svg";
import { ReactComponent as Close } from "../../../assets/Icons/icon-chevron-up.svg";
import { ReactComponent as Open } from "../../../assets/Icons/icon-chevron-down.svg";
import { useSelector, useDispatch } from "react-redux";
import modalSlice, {
closeViewTaskModal,
openEditTaskModal,
closeAllModals,
openDeleteTaskModal,
} from "../../../features/global/modalSlice";
import Backdrop from "../Backdrop/Backdrop";
import Subtask from "../../Task/Subtask";
import "../../Extra/DropdownSettings.scss";
import { useOutsideClick } from "../../../hooks/useOutsideClick";
import { motion } from "framer-motion";
import DropdownStatus from "../../Extra/DropdownStatus";
import DropdownSettings from "../../Extra/DropdownSettings";
import DropdownSettingsTask from "../../Extra/DropdownSettingsTask";
const ViewTaskModal = ({ handleClose }) => {
const [openSettings, setOpenSettings] = useState(false);
const dispatch = useDispatch();
const task = useSelector((state) => state.modal.viewTaskModal.task);
const handleCloseSettings = () => {
console.log("hi");
setOpenSettings(false);
};
const modal = useSelector((state) => state.modal);
const viewTaskModal = useSelector((state) => state.modal.viewTaskModal);
const elipsisRef = useRef(null);
const wrapperRef = useOutsideClick(handleCloseSettings, elipsisRef);
const getFinishedSubTasks = () => {
let finishedSubTasks = 0;
task.subTasks.forEach((subtask) => {
if (subtask.isDone) {
finishedSubTasks++;
}
});
return finishedSubTasks;
};
const closeModal = () => {
dispatch(closeViewTaskModal());
};
return (
<Backdrop onClick={closeModal} mobile={false}>
<motion.div
onClick={(e) => {
e.stopPropagation();
}}
onMouseDown={(e) => {
e.stopPropagation();
}}
className="view-task"
>
<div className="view-task__header | flex">
<h2 className="view-task__header__title">{task.name}</h2>
<div className="view-tastk__settings">
<div
className="view-task__header__icon"
style={{ cursor: "pointer" }}
ref={elipsisRef}
onClick={() => {
setOpenSettings(!openSettings);
}}
>
<Elipsis />
</div>
{openSettings && (
<div className="dropdown-settings__task" ref={wrapperRef}>
<div
className="dropdown-settings__item"
onClick={() => {
dispatch(closeAllModals());
dispatch(openEditTaskModal(task));
}}
>
Edit Task
</div>
<div
className="dropdown-settings__item"
onClick={() => {
dispatch(closeAllModals());
dispatch(openDeleteTaskModal(task));
}}
>
Delete Task
</div>
</div>
)}
</div>
</div>
<p className="view-task__description">{task.description}</p>
<div className="view-task__subtasks">
<p>
Subtasks ({getFinishedSubTasks()} of {task.subTasks.length})
</p>
<div className="view-task__subtasks__list">
{task.subTasks.map((subtask, index) => (
<Subtask
subtaskID={subtask.id}
boardID={task.boardID}
taskID={task.id}
columnID={task.columnID}
key={index}
/>
))}
</div>
</div>
<div className="view-task__status">
<p>Current Status</p>
<DropdownStatus click={handleCloseSettings} task={task} />
</div>
</motion.div>
</Backdrop>
);
};
export default ViewTaskModal;
How would you add a component inside an useRef object (which is refering to a DOM element)?
const Red = () => {
return <div className="color">Red</div>;
};
const Black = () => {
return <div className="color">Black</div>;
};
const Green = () => {
return <div className="color">Green</div>;
};
const Button = (params) => {
const clickHandler = () => {
let boolA = Math.random() > 0.5;
if (boolA) {
params.containerRef.current.appendChild(<Red />);
} else {
let boolB = Math.random() > 0.5;
if (boolB) {
params.containerRef.current.appendChild(<Black />);
} else {
params.containerRef.current.appendChild(<Green />);
}
}
};
return <button onClick={clickHandler}>Click</button>;
};
export default function App() {
const containerRef = useRef(null);
return (
<div className="App">
<Button containerRef={containerRef} />
<div ref={containerRef} className="color-container">
Color components should be placed here !
</div>
</div>
);
}
params.containerRef.current.appendChild(); -> throws an error. I`ve put it to show what I would like to happen.
Also is what I`m doing an anti-pattern/stupid ? Is there another (smarter) way of achieving the above ?
codesandbox link
edit :
I forgot some important information to add.
Only Button knows and can decide what component will be added.
expecting you want to add multiple colors, something like this would work and don't need the ref:
import { useState } from "react";
import "./styles.css";
const Color = () => {
return <div className="color">Color</div>;
};
const Button = (params) => {
return <button onClick={params.onClick}>Click</button>;
};
export default function App() {
const [colors, setColors] = useState([]);
return (
<div className="App">
<Button onClick={() => setColors((c) => [...c, <Color />])} />
<div className="color-container">
{colors}
</div>
</div>
);
}
It's better to have a state that is changed when the button is clicked.
const [child, setChild] = useState(null);
const clickHandler = () => {
setChild(<Color />);
};
const Button = (params) => {
return <button onClick={params.onClick}>Click</button>;
};
<Button onClick={clickHandler} />
<div className="color-container">
Color components should be placed here !
{child}
</div>
Working sandbox
Edit: Refer to #TheWuif answer if you want multiple Colors to be added upon clicking the button repeatedly
There're several things from your code I think are anti-pattern:
Manipulate the real dom directly instead of via React, which is virtual dom
Render the Color component imperatively instead of declaratively
Here's the code that uses useState (state displayColor) to control whether <Color /> should be displayed
import { useState } from "react";
import "./styles.css";
const Color = () => {
return <div className="color">Color</div>;
};
const Button = (props) => {
return <button onClick={props.clickHandler}>Click</button>;
};
export default function App() {
const [displayColor, setDisplayColor] = useState(false);
const clickHandler = () => {
setDisplayColor(true);
};
return (
<div className="App">
<Button clickHandler={clickHandler} />
<div className="color-container">
Color components should be placed here !{displayColor && <Color />}
</div>
</div>
);
}
Codesandbox
I'm trying to build edit form fields. Here, you have to press a button before you can edit the form field. However, I'm having problems toggling the disabled prop and setting the focus of the element. This is some sample code. The input will only focus after I've clicked the button twice.
export default function App() {
const [isDisabled, setIsDisabled] = useState(true);
const inputEl = useRef(null);
const onBlur = () => {
setIsDisabled(true);
};
const handleEditClick = () => {
setIsDisabled(false);
inputEl.current.focus();
};
return (
<div className="App">
<h1>Focus problem</h1>
<h2>Focus only happens on the scond click!</h2>
<input ref={inputEl} onBlur={onBlur} disabled={isDisabled} />
<button onClick={() => handleEditClick()}>Can edit</button>
</div>
);
}
Here is a code-sandbox
Setting focus on a DOM element is a side-effect, it should be done within useEffect:
import React, { useState, useRef, useEffect } from "react";
import "./styles.css";
export default function App() {
const [isDisabled, setIsDisabled] = useState(true);
const inputEl = useRef(null);
useEffect(() => {
inputEl.current.focus();
}, [isDisabled]);
const onBlur = () => {
setIsDisabled(true);
};
const handleEditClick = () => {
setIsDisabled(false);
};
return (
<div className="App">
<h1>Focus problem</h1>
<h2>Focus only happens on the scond click!</h2>
<input ref={inputEl} onBlur={onBlur} disabled={isDisabled} />
<button onClick={() => handleEditClick()}>Can edit</button>
</div>
);
}
I am use bootstrap modal in reactjs project. Here is the link of package which i have installed in my project: https://www.npmjs.com/package/react-responsive-modal
When, I click on open the modal button then it is working, but when i click on close button then close button is not working. I am using the hooks in my project. Below, I have mentioned my code:
import React, { useState } from 'react'
import Modal from 'react-responsive-modal'
const Login = () => {
const [open, openModal] = useState(false)
const onOpenModal = () => {
openModal({open: true})
};
const onCloseModal = () => {
openModal({open: false})
};
return(
<div>
<h1>Login Form</h1>
<button onClick={onOpenModal}>Open modal</button>
<Modal open={open} onClose={onCloseModal} center>
<h2>Simple centered modal</h2>
</Modal>
</div>
)
}
export default Login;
The issue is because, you are setting object in state,
openModal({open: true})
This will store object in state.
setState require's direct value which needs to be change, your setState must be this,
const onOpenModal = () => {
openModal(!open) //This will negate the previous state
};
const onCloseModal = () => {
openModal(!open) //This will negate the previous state
};
Demo
You can simplify your code and just use 1 change handle for your modal,
const Login = () => {
const [open, openModal] = useState(false)
const toggleModal = () => {
openModal(!open)
};
return(
<div>
<h1>Login Form</h1>
<button onClick={toggleModal}>Open modal</button>
<Modal open={open} onClose={toggleModal} center>
<h2>Simple centered modal</h2>
</Modal>
</div>
)
}
Demo
Your naming of the model hook is misleading and you're using the setState part of the Hook wrong, probably mixing it up with the this.setState convention for non-Hook React code.
import React, { useState } from 'react'
import Modal from 'react-responsive-modal'
const Login = () => {
const [modalOpen, setModalOpen] = useState(false)
const onOpenModal = () => {
setModalOpen(true)
};
const onCloseModal = () => {
setModalOpen(false)
};
return(
<div>
<h1>Login Form</h1>
<button onClick={onOpenModal}>Open modal</button>
<Modal open={modalOpen} onClose={onCloseModal} center>
<h2>Simple centered modal</h2>
</Modal>
</div>
)
}
export default Login;