I'm trying to add a clickable image to close a material-ui dialog.
The props.onRequestClose works properly when I click outside the dialog but the onClick doesn't response.
What am I missing?
const ChangePasswordDialog = (props) => (
<Dialog open={props.open} onRequestClose={props.onRequestClose} modal={false}>
<div className="close-popup">
<Svg onClick={props.onRequestClose} viewBox="0 0 22.75 22.75">{closePopup}
</Svg >
</div>
</Dialog>
);
ChangePasswordDialog.propTypes = {
open:PropTypes.bool.isRequired,
onRequestClose:PropTypes.func.isRequired
};
I solved it by adding a prop called onClick to the Svg components.
Here is the code for the Svg components if someone needs:
class Svg extends Component{
render() {
const {children, viewBox, onClick} = this.props;
return(
<div onClick={onClick}>
<SvgIcon viewBox={viewBox}>
{children}
</SvgIcon>
</div>
)
};
}
Svg.propTypes = {
children:PropTypes.any.isRequired,
viewBox:PropTypes.string,
onClick:PropTypes.function
};
export default Svg;
Related
How can I Change styles on hover of an appropriate item react? Now the styles are changed of all of the items at a time. Hovering on add to cart button I need to change only the chosen div styles.
https://codesandbox.io/s/trusting-moon-djocul?file=/src/components/Category.js**
import React, { useState } from "react";
import styles from "./category.module.css";
import Categories from "./Categories";
const Category = () => {
const [hovered, setHovered] = useState(false);
const [data, setData] = useState(Categories);
const style = hovered
? { backgroundColor: "#ffcbcb", color: "#fff", transition: "all 0.5s ease" }
: {};
const filterResult = (catItem) => {
const result = Categories.filter((curData) => curData.category === catItem);
setData(result);
};
return (
<>
<div className={styles.items}>
{data.map((value) => {
const { id, title, price, description } = value;
return (
<>
<div className={styles.item} key={id} style={style}>
<h1>{title}</h1>
<p>
{price} <span>$</span>
</p>
<p>{description}</p>
<button
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
className={styles.btn}
>
Add to Cart
</button>
</div>
</>
);
};
export default Category;
That's because you have a single "hovered" state shared between all your cards, you should create a "Card" component and have that component have its own hovered state
return (
<>
<div className={styles.items}>
{data.map((value) => {
return (
<>
<Card {...props} />
</>
);
Problem
This issue is occurring cause you are applying CSS to all the cards. The only small thing you are missing in your logic is adding CSS to the only card whose button was being hovered.
Solution
That can be achieved by using event objects on mouse enter and mouse leave events.
<div className={styles.item} key={id} style={style}>
<h1>{title}</h1>
<p>
{price} <span>$</span>
</p>
<p>{description}</p>
<button
onMouseEnter={(e) =>
e.currentTarget.parentElement.classList.add("active_card")
}
onMouseLeave={(e) =>
e.currentTarget.parentElement.classList.remove("active_card")
}
className={styles.btn}
>
Add to Cart
</button>
</div>
This will add a class of "active_card" on the card whose Add To Card button is being hovered, then you can apply your desired styling to that class.
.active_card {
background-color: #ffcbcb !important;
}
Example
Working Code Sandbox Example
So basically I want when I press on the Button I want to Icon to disappear as well as Button. I have tried something but clearly, it is not working, so I would appreciate some help if possible
const Button = (props) => {
const[toggleIcon, setToggleIcon]=React.useState('true')
function Icon() {
toggleIcon(false)
}
return(
<div>
<button onClick={()=> setToggleIcon('false')}></button>
</div>
)
}
export default Button
const Icon = (props) => {
return(
<div>
<Icon>{props.Icon('false')}</Icon>
</div>
export default Icon
You can prevent a component from rendering by returning null. Use conditional rendering with the toggleIcon state as the condition. The onClick handler changes the state.
E.G.:
const Button = () => {
const [toggleIcon, setToggleIcon] = React.useState(true)
return toggleIcon ? (
<button onClick={() => setToggleIcon(false)}><Icon /></button>
) : null
}
export default Button
Take a look at the Conditional Rendering section of the React docs.
I have created a component in my ReactJS app with a Button and a div. My goal is to press the button and show/hide the div, which currently works. But I will re-use the component so I will have multiple Buttons and divs. But I always only want one div to show.
If I press a button, hide all the divs from the same component and show the div from the button I just pressed, otherwise if the button I just pressed div is open hide it. It work the same as Bootstrap's dropdown button, but I prefer not to use Bootstrap's dropdown as I would like to create my own custom button.
I import the below Hide component in my App.js file. It works by hiding or showing the div, but would like to hide all other open div's apart from the button I currently pressed if it is not open yet.
Here is the code I currently have and use my Mycomp twice,
function hide () {
return (
<div>
<Mycomp />
<Mycomp />
</div>
);
}
function Mycomp(){
const[dp, setDp] = useState("none");
const toggle = () => {
if (dp === "none"){
setDp("block")
}else{
setDp("none")
}
}
return(
<div>
<button onClick={toggle}>Test</button>
<div style={{display: dp}}>{dp}</div>
</div>
)
}
export default hide;
You can change your component this way to get what you want . try to run this code to see the result
function Hide() {
const [visibleDivIndex, setVisibleDivIndex] = React.useState(0);
const handleVisibleDiv = id => setVisibleDivIndex(id);
const divArr = [1, 2, 3]; // just to show haw many component we will use
return (
<div>
{divArr.map((item, index) => (
<Mycomp
key={index}
id={index}
visibleDivIndex={visibleDivIndex}
handleVisibleDiv={handleVisibleDiv}
/>
))}
</div>
);
}
function Mycomp({ id, visibleDivIndex, handleVisibleDiv }) {
return (
<div>
<button onClick={e => handleVisibleDiv(id)}>Test</button>
<div style={{ display: id === visibleDivIndex ? "block" : "none" }}>
My Div Content
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<Hide />
</React.StrictMode>,
rootElement
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
The basic idea would be to move the state up to the container.
function Hide() {
const defaultDisplay = () => Array.from({length: 2}).fill(false);
const [displays, setDisplays] = useState(defaultDisplay());
function onClick(id) {
const temp = defaultDisplay();
temp[id] = true;
setDisplays(temp);
}
return (
<div>
{
displays.map((display, i) => {
return <Mycomp display={display} id={i} onClick={onClick} />;
}
}
</div>
);
}
function Mycomp({display, id, onClick}) {
const[dp, setDp] = useState(display);
useEffect(() => {
setDp(display ? 'block' : 'none');
}, [display]);
return(
<div>
<button onClick={() => onClick(id)}>Test</button>
<div style={{display: dp}}>{dp}</div>
</div>
)
}
export default Hide;
I am trying to make a modal reusable:
this is my component:
class OverleyModal extends Component {
constructor(props) {
super(props);
}
openModal = () => {
document.getElementById("myOverlay").style.display = "block";
}
closeModal = () => {
document.getElementById("myOverlay").style.display = "none";
}
render() {
return (
<React.Fragment>
<div id="myOverlay" className="overlay">
<div className="overlay-content">
<p>content goes there</p>
</div>
</div>
</React.Fragment>
)
}
}
export default OverleyModal;
The above component is working great for the purpose of modal, that is why i didn't include here CSS/style, the issue not about CSS.
I want, when i mount this component on any compoenet like thise below:
<overleyModal open={true} />
if open=true, the modal will be visiable
<overleyModal open={false} />
and if open={false}
the modal will disappear
You can see how i deal open and close modal in the coponent method openModal() and closeModal()
But i am going through the trouble to make it reliable, I just want to use open as props, nothing else. if open is true, it will appear and if false, it will disappear.
Can anyone please help me in this case?
You need to make use of props and control the opening and closing through it by conditionally rendering it. You can also make the content generic by passing it as props too
class OverlayModal extends Component {
render() {
const { open, content } = this.props
return open? <React.Fragment>
<div id="myOverlay" className="overlay">
<div className="overlay-content">
<p>{content}</p>
</div>
</div>
</React.Fragment>: null
}
}
export default OverlayModal;
and use it like
<OverlayModal open={true} content={content goes there'} />
or even better you can define the content as children to give you more styling options
class OverlayModal extends Component {
render() {
const { open, children} = this.props
return open? <React.Fragment>
<div id="myOverlay" className="overlay">
<div className="overlay-content">
{children}
</div>
</div>
</React.Fragment>: null
}
}
export default OverlayModal;
and using as
<OverlayModal open={true} ><p>content goes there</p></OverlayModal >
open can be a property value and modal can be rendered conditionally based on the prop value. There is no need to directly access dom element.
return props.open ? (
<div id="myOverlay" className="overlay">
<div className="overlay-content">
<p>content goes there</p>
</div>
</div>
) : null;
I am trying to create something similar to React Bootstrap's dropdown component. My starting skeleton is something like the following:
import React from 'react';
const DropDown = props => {
return <div className="dropdown-container">{props.children}</div>;
};
const DropDownToggle = props => {
return <div className="dropdown-toggle">{props.children}</div>;
};
const DropDownContent = props => {
return <div className="dropdown-content">{props.children}</div>;
};
export { DropDown, DropDownToggle, DropDownContent };
These components would be used like this:
<DropDown>
<DropDownToggle>
{/*
The content inside here should be customizable so the user of
these components can specify whatever they want for the toggle
*/}
<button type="button">
my button
</button>
</DropDownToggle>
<DropDownContent>
{/*
The content inside here should be customizable so the user of
these components can specify whatever they want for the content of
the dropdown
*/}
<ContentComponent/>
</DropDownContent>
</DropDown>
Is there a way I can communicate between the two children components (DropDownContent and DropDownToggle)? I have access to the parent component and it just receives and displays the children so far, but I would like to somehow communicate between the children so that the user can click on the toggle to open/close the content. I don't want to use redux.
Thank you in advance!
EDIT
I ended up going with the method that #Train suggested in his/her comment below. I was originally hoping for the ability to nest components manually, but what was most important to me was having the state be self-contained in the parent component. Being able to define the toggle button's HTML as well as the content's HTML was also a requirement. My final implementation allows for both of these things and looks something like this:
import React from 'react';
import PropTypes from 'prop-types';
export class Dropdown extends React.Component {
state = {
isOpen: false,
};
onDropDownToggleClick = () => {
this.setState({ isOpen: !this.state.isOpen });
};
render() {
let contentClasses = 'dropdown-content';
if (this.state.isOpen) {
contentClasses += ' show';
}
return (
<div className="dropdown-container">
<div className="dropdown-toggle" onClick={this.onDropDownToggleClick}>
{this.props.toggle}
</div>
<div className={contentClasses}>{this.props.content}</div>
</div>
);
}
}
Dropdown.propTypes = {
toggle: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
content: PropTypes.oneOfType([PropTypes.string, PropTypes.element])
.isRequired,
};
export default Dropdown;
to use it:
const dropDownToggle = (
<button type="button">
Dropdown
</button>
);
const dropDownContent = 'content';
<DropDown
toggle={dropDownToggle}
content={dropDownContent}
/>
For something like toggling content you can use composition instead of inheritance to pass data around.
From the example of Facebook
This is done with props.children property.
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
In the render() I am rendering the Dialog component and passing in the props.
the props are .children and the custom props title, message
This lets us pass child elements directly into the output we can even add components from other classes as I did with the SignUpDialog.
Did you have something like this in mind?
const actionTypes = {
TOGGLE: "TOGGLE"
};
const notRedux = {
actionHandlers: Object.keys(actionTypes).reduce(
(acc, val) => ({ [val]: [], ...acc }),
{}
),
dispatchAction(actionType, data) {
this.actionHandlers[actionType].forEach(handler => handler(data));
},
onAction(actionType, actionHandler) {
this.actionHandlers[actionType].push(actionHandler);
}
};
const DropDown = ({ children }) => {
return <div className="dropdown-container">{children}</div>;
};
const DropDownToggle = () => {
const onClick = () =>
notRedux.dispatchAction(actionTypes.TOGGLE, "oh hi Mark");
return (
<div className="dropdown-toggle">
<button type="button" onClick={onClick}>
my button
</button>
</div>
);
};
const DropDownContent = props => {
notRedux.onAction(actionTypes.TOGGLE, data =>
alert(`DropDownToggle said ${data} //DropDownContent`)
);
return <div className="dropdown-content">{props.children}</div>;
};
const App = () => (
<DropDown>
<DropDownToggle></DropDownToggle>
<DropDownContent>
<span>Content goes here</span>
</DropDownContent>
</DropDown>
);
ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></app>