React - Changing CSS property on click (arrow function) - javascript

I have the following in one of my React / Gatsby files:
import React from "react"
const click = () => {
console.log("J");
}
const NavButton = () =>
<button className="navbar-toggler navbar-toggler-right" style={{backgroundColor: 'blue', position: "absolute", margin: "30px"}}type="button" data-toggle="collapse" data-target="#collapsingNavbar" onClick={click}>
<div id="nav-icon1">
<span></span>
<span></span>
<span></span>
</div>
</button>
const Dropdown = () =>
<div style={{visibility: "hidden", backgroundColor: "blue", position: "absolute", height: "100%", width: "100%"}}>
</div>
export default (props) =>
<div className="left col-xs-12 col-md-6">
<Dropdown />
<NavButton />
{props.children}
</div>
Now I would like to fire click() whenever somebody presses the NavButton, and then I would like to make Dropdown visible. How would I do this? Right now I have it hardcoded that Dropdown has style={{visibility: "hidden", ....
I'm also wondering whether I am doing this correctly, having everything loosely in these different functions, if somebody could tell me that would be great!

Your controlling class needs to be stateful: it needs to maintain the boolean state as to whether the dropdown is open or closed. When rendering the dropdown, if the boolean is open, then you'll show the dropdown, else you won't.
Here is your code rewritten to do this. Note the child components take props as arguments. This is how the parent communicates with them. Some of those props are callbacks, this is how the child communicates back to the parent.
import React from "react"
const NavButton = ({onClick}) =>
<button className="navbar-toggler navbar-toggler-right" style={{backgroundColor: 'blue', position: "absolute", margin: "30px"}}type="button" data-toggle="collapse" data-target="#collapsingNavbar" onClick={onClick}>
<div id="nav-icon1">
<span></span>
<span></span>
<span></span>
</div>
</button>
const Dropdown = ({show}) =>
<div style={{visibility: show ? "visible" : "hidden", backgroundColor: "blue", position: "absolute", height: "100%", width: "100%"}}>
</div>
export default class Parent extends React.Component {
state = {
dropdownVisible: false,
};
// toggles the dropdown each time it is called
toggleDropdown = () => this.setState(state => ({
dropdownVisible: !state.dropdownVisible,
}));
render() {
return (
<div className="left col-xs-12 col-md-6">
<Dropdown show={this.state.dropdownVisible} />
<NavButton onClick={this.toggleDropdown} />
{this.props.children}
</div>
);
}
}

Related

I created a Modal using createPortal() method to render it. Then I found that modal renders twice

When the button inside the Post clicked, Popup will render with createPortal method outside from root element's tree.
With this code that popup renders twice.
I want to render it only once.
Here's the parent Post component.
import { useState } from 'react';
import PopupModal from './PopupModal/PopupModal';
import './Post.css';
const Post = (props) => {
const postData = props;
const [isOpen, setIsOpen] = useState(false);
return (
<div className="post-container">
<div className="post-img-container">
<img className="post-img" src={props.img} alt="Travels" />
</div>
<div className="post-text-container">
<h4 className="post-heading">{props.title}</h4>
<p className="post-para">{props.description}</p>
<h1 className="post-price">{props.price}</h1>
<div className="post-btn-container">
<button onClick={() => setIsOpen(true)} className="post-btn">
Check Availability
</button>
<PopupModal dataData={postData} open={isOpen} onClose={() => setIsOpen(false)}>
Button123
</PopupModal>
</div>
</div>
</div>
);
};
export default Post;
And here's the popupModal
import React from 'react';
import ReactDOM from 'react-dom';
import '../PopupModal/popupModal.css'
const MODAL_STYLES = {
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%,-50%)',
background: '#fff',
width: '40vw',
height: '90vh',
padding: '50px',
zIndex: 1000,
};
const PopupModal = ({ open, children, onClose ,dataData }) => {
if (!open) return null;
console.log('xxx');
console.log(dataData);
return ReactDOM.createPortal(
<>
<div className='modal-overlay' ></div>
<div className='modal-container'>
<button onClick={onClose}> Popup Close</button>
{children}
</div>
</>,
document.getElementById('portal')
);
};
export default PopupModal;
Here's how I figured it rendered twice.
Here's the Popup with overlay around it which covers the background.
Thanks in advance!
Try following
{
isOpen && <PopupModal dataData={postData} open={isOpen} onClose={() => setIsOpen(false)}>
Button123
</PopupModal>
}

How to efficiently toggle classes with multiple HTML elements across multiple files

I am toggling an arrow image whenever my div is clicked. I am controlling the state of the click with
const [toggleArrow, setToggleArrow] = useState(false)
My div has an onClick function that controls the state and toggles the faq-subject-toggle-arrow class that adds an image
<div className={`faq-subject ${toggleArrow ? 'faq-subject-toggle-arrow' : ''}`} onClick={() => {
setToggleArrow(!toggleArrow)
}>
My problem is that I have 50 divs across multiple styles and don't want to make 50 states to toggle one image.
Is there a more efficient solution for this with less code?
I created something that does the same thing. I extracted your code and made a component. now the state lives inside the component and will not affect others.
live demo
component
import { useState } from "react";
export const Button = () => {
const [toggleArrow, setToggleArrow] = useState(false);
return (
<div
style={{ width: "50px", height: "50px", margin: "10px" }}
className={`faq-subject ${toggleArrow ? "faq-subject-toggle-arrow" : ""}`}
onClick={() => {
setToggleArrow(!toggleArrow);
}}
></div>
);
};
css file, I didn't have the same classes so I created mine.
.faq-subject {
background: blue;
}
.faq-subject-toggle-arrow {
background: orange;
}
now you can use it wherever you want for multiple times
import { Button } from "./button";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Button />
<Button />
<Button />
<Button />
<Button />
</div>
);
}

i would like to make an individual process in a map function like toggle on click

i want to make a button toggle on each div i click in map function but the buttons toggle in
whole dives on one click (i want to make each click toggle abutton on only the div that i clicked not all of them) ...................................................................................................
import React,{Component} from 'react'
import './course.css'
import Downloadcourse from './../download/down.js'
import {Transition,animated} from 'react-spring/renderprops'
class Course extends Component{
constructor(){
super();
this.state={
search:'',
showcomponent:false,
};
}
updatesearch=(e)=>{
this.setState({search:e.target.value.substr(0,20)});
}
downtoggle=(index)=>{
this.setState({showcomponent:!this.state.showcomponent})
}
render(){
let filteredcontacts= this.props.corses.filter(
(item)=>{
return item.name.toLowerCase().indexOf(
this.state.search.toLowerCase()) !== -1 ;
}) ;
let length= this.props.corses.length;
const courselist=length ?(
filteredcontacts.map((item,index)=>{
return (
<div className="col-sm-3 cat" key={item.id}>
<div className="maincover" >
<div className="mainimg" onClick={this.downtoggle}>
</div>
<div className="maincorse">
<div className="row course-title">
<h1 className="course-Name">{item.name}</h1>
</div>
<div className="row course-title">
<button className="removeing" onClick={()=>{this.props.deleteing(index)}}>Remove
Course</button>
</div>
<div className="row download">
<Transition
native
items={this.state.showcomponent}
from={{ position: 'absolute', overflow: 'hidden', height: 0 }}
enter={[{ height: 'auto' }]}
leave={{ height: 0 }}>
{show=>show &&(props=>(
<animated.div style={props}>
<Downloadcourse />
</animated.div>
))}
</Transition>
</div>
</div>
</div>
</div>
)}
)) :(
<div className="no-content">
<h2>no content to show</h2>
</div>
)
return(
<div className="course">
<input type="text" className="input-search" onChange={this.updatesearch}/>
<span className="magnficant"> 🔍</span>
<div className="row category">
{courselist}
</div>
</div>
)
}
}
export default Course;
To make multiple elements toggleable you need to store each element's toggled state in some data structure. A javascript object ({}) comes in handy for this.
Convert this.state.showComponent from boolean to object
this.state={
search: '',
showComponent: {},
};
Use the index passed to toggle handler to update toggled state
downtoggle = (index) => {
this.setState(prevState => ({
showComponent: {
...prevState.showComponent, // <-- spread existing state
[index]: !prevState.showComponent[index], // <-- toggle value
},
}));
}
Ensure index is passed to the toggle handler
onClick={() => this.downtoggle(index)}
Check the toggled state for the transition component
<Transition
// ... other props
items={this.state.showComponent[index]} // <-- truthy/falsey
/>

Is there a way to control where a nested Material UI select in a popper gets mounted in the DOM?

I am trying to place a select menu in a Popper. The issue I'm running into is that the nested select menu wants to mount the popup that comes out of it on the body as a neighbor and not a child of popper. This causes the clickaway event to fire. Here's the code to reproduce it:
import React, { useState } from "react";
import "./styles.css";
import Popper from "#material-ui/core/Popper";
import TextField from "#material-ui/core/TextField";
import MenuItem from "#material-ui/core/MenuItem";
import ClickAwayListener from "#material-ui/core/ClickAwayListener";
export default function App() {
const [popperAnc, setPopperAnc] = useState(null);
const popperOpen = Boolean(popperAnc);
return (
<div className="App">
<div
onClick={e => {
setPopperAnc(e.currentTarget);
}}
>
Popper anchor
</div>
<div style={{ position: "absolute" }}>
<Popper open={popperOpen} anchorEl={popperAnc}>
<ClickAwayListener
onClickAway={e => {
setPopperAnc(null);
}}
>
<TextField select label="Menu">
<MenuItem value="select1">Select me!</MenuItem>
</TextField>
</ClickAwayListener>
</Popper>
</div>
</div>
);
}
https://codesandbox.io/s/strange-bassi-liwdc?file=/src/App.js:0-1013
If you need to use Select - just use it (don't use TextField, it doesn't make any sense).
You need to make sure that the second popper is not rendered as a portal (you need to set disabledPortal on the MenuProps of the Select element.
You need to tell the new menu where to position itself and what will be it's size.
<div style={{ position: "absolute" }}>
<Popper open={popperOpen} anchorEl={popperAnc}>
<ClickAwayListener
onClickAway={e => {
console.log("click away");
setPopperAnc(null);
}}
>
<div>
<div>Wow</div>
<Select
label="Menu"
MenuProps={{
disablePortal: true,
anchorEl: this,
style: { marginTop: "20px", width: "150px", height: "200px" }
}}
>
<MenuItem value="select1">Select me!</MenuItem>
</Select>
</div>
</ClickAwayListener>
</Popper>
</div>
Here is a working example: https://codesandbox.io/s/mui-nested-popper-4uu5l?file=/src/App.js

Rendering component on long mouse click

I am trying to render a modal component on a long mouse click. If I just try to fire an alert it works but rendering doesn't seem to do the trick. I am assuming maybe If I have to return? Not quite sure. I created a function handleButtonPressDown to perform this task and the handleButtonRelease to clear interval in the event the user decides not to perform this action.
export class Dropdown extends React.Component<IProps> {
buttonPressTimer: any;
constructor(props: IProps) {
super(props);
this.handleButtonPress = this.handleButtonPress.bind(this);
this.handleButtonRelease = this.handleButtonRelease.bind(this);
}
public render() {
return (
<div style={{ alignSelf: "center" }}>
<ul className="nav nav-pills">
{filteredProbes.length === 0 ? (
<li className="nav-item dropdown ">
<div
className="dropdown-menu show"
x-placement="bottom-start"
style={{
display: "none"
}}
></div>
</li>
) : (
<li className="nav-item dropdown ">
<div
className="dropdown-menu show"
x-placement="bottom-start"
style={{
position: "relative",
willChange: "transform",
top: "5px",
overflowY: "scroll",
maxHeight: "200px",
color: "white"
}}
>
{this.props.searchState.isActive === false
? probes.map(probe => (
<a
onClick={() => this.props.onUpdateSelectedProbe(probe)}
className="dropdown-item"
onMouseDown={this.handleButtonPress}
onMouseUp={this.handleButtonRelease}
>
<div
className="dropdown-divider"
style={{ backgroundColor: "black" }}
></div>
{probe.companyPN}: {probe.description}
</a>
))
: filteredProbes.map(filterprobe => (
<a
onClick={() =>
this.props.onUpdateSelectedProbe(filterprobe)
}
className="dropdown-item"
>
<div className="dropdown-divider"></div>
{filterprobe.companyPN}: {filterprobe.description}
</a>
))}
</div>
</li>
)}
</ul>
</div>
);
}
handleButtonPress() {
this.buttonPressTimer = setTimeout(() => {
{/* Show the modal if showModal is true */}
this.props.modalState.showModal && (
<WedgeGroup
wedgeState={this.props.wedgeState}
onUpdateSelectedWedge={this.props.onUpdateSelectedWedge}
onUpdateShowModal={this.props.onUpdateShowModal}
onUpdateHideModal={this.props.onUpdateHideModal}
modalState={this.props.modalState}
/>
);
}, 1000);
}
handleButtonRelease() {
clearTimeout(this.buttonPressTimer);
}
}
You need to move the code that you have inside setTimeout to render function and use state to render WedgeGroup:
export class Dropdown extends React.Component<IProps> {
...
constructor(props: IProps) {
super(props);
this.state = {
showModal: false
};
...
}
public render() {
const showModal = this.props.modalState.showModal &&
this.state.showModal;
return (
<div style={{ alignSelf: "center" }}>
{
showModal && (
<WedgeGroup
wedgeState={this.props.wedgeState}
onUpdateSelectedWedge={this.props.onUpdateSelectedWedge}
onUpdateShowModal={this.props.onUpdateShowModal}
onUpdateHideModal={this.props.onUpdateHideModal}
modalState={this.props.modalState}
/>
);
}
//..... render other components
</div>
);
}
handleButtonPress() {
this.buttonPressTimer = setTimeout(() => {
this.setState({
showModal: true
})
}, 1000);
}
handleButtonRelease() {
clearTimeout(this.buttonPressTimer);
}
}
It will not render firstly because your are not triggering any mechanism that makes React render.
I'd suggest to you to remove this component from the setTimeout, place it inside the render (where it should be).
And finally manipulate your component state to show or hide your modal.
If you trigger a timer to show the modal view it will only appear after the change of the state, so in your case it will take 1s to show to the user, what may look not responsive.
// inside your handleButtonPress()
this.setState({
showModal: true
}}

Categories

Resources