How to toggle style property using UseState - javascript

In react I would like to lock scroll-bar when sub-menu is out and then again unlock it when menu is hidden. I press the same button to open and close sub-menu (adds and removes translate for this element).
With what i did page sucessfully locks but when i press menu button and sub-menu dissapears the page is still locked, how can i change that the code is below:
const [isActive, setActive] = useState(false);
const toggleClass = () => {
setActive(!isActive);
document.body.style.overflow = "hidden"
};
<div className="button"
onClick={toggleClass}
className={
isActive ? "nav-button closebtn active" : "nav-button closebtn"}>
<span></span>
</div>
how can I toggle the value of overflow from hidden to unset by pressing the button, just like i do with adding the class when i press the same button

Use toggle and add style in CSS file.
import './myStyle.css';
const [isActive, setActive] = useState(false);
const toggleClass = () => {
setActive(!isActive);
document.body.classList.toggle('lock', isActive); // <-- Add this
};
<div className="button"
onClick={toggleClass}
className={
isActive ? "nav-button closebtn active" : "nav-button closebtn"}>
<span></span>
</div>
// myStyle.css
.lock {
overflow: hidden;
}

I believe that in your togle you have to validate if it is active or not and in that change the hidden to visible
Something like:
const toggleClass = () => {
setActive((active) => {
if(active){
document.body.style.overflow = "visible";
return !active;
}
document.body.style.overflow = "hidden";
return !active;
});
};

Related

Trigger animated div each time is called with React / Next.js

I'm new with React and Next.js, I'm creating a timeline, the timeline has colored bars which if pressed, the page shows a div below with info about each bar.
I managed to make the content below appear and disappear with useState hook so the content of each bar doesn't stack, I'm using an animated tag "Section" and only the first time I press any bar, the content is animated, the rest appears statically, I'm wondering if I can use something like the useEffect hook to maybe re-render each content div so the animation appears every time you click each bar, also to erase the last loaded div so doesn't stack on browser memory, I hope I explained myself I started with React 2 days ago, and thank you for your time.
Example reduced code:
//useState hook
const [content, setContent] = useState(null);
//Timeline section
<div>
<Bar1 onClick={() => setContent(BarContent_1)}/>
<Bar2 onClick={() => setContent(BarContent_2)}/>
</div>
//Content display
<div>
{content}
</div>
//Content of each bar, (<Section> div has the animation)
const BarContent_1 = () => {
return (
<Section>
Content of bar 1
</Section>
)
}
const BarContent_2 = () => {
return (
<Section>
Content of bar 2
</Section>
)
}
you can use useState for that to toggle classList on element here is example below what I do, in your project I don't know what you do but I will come up with card example first as your default value set state to false in card component and when you will click the button toggle that boolean false to true
like that () => setState(!state) it will always set the value of opposite state
when you will change state component always re-renders and checks the state, and you will make like that on more info div, if that state is true show more-info active that means your div will be displayed
and if it is false it will be dissapeared
const Card1 = () => {
const [showMore, setShowMore] = useState(false)
return (
<div className="card">
<div>Daniel</div>
<button onClick={() => setShowMore(!showMore)}>Show More</button>
<div class={showMore ? "more-info active": "more-info"}>This is more info Daniel</div>
</div>
)
}
also here is my css what I do
.more-info{
opacity: 0;
transition: 0.5s ease;
}
.active{
opacity: 1
}
in start thats good to make stuff like that but I would recommend you to use array objects for that to .map() components and make toggling animations on that, good luck
also quickly I made working example on sandbox
#callmenikk gave me an idea to use a conditional to render my styled div every time the condition met.
Solution:
//useState hook
const [content, setContent] = useState(null);
//Conditional function 'Refresh'
const Refresh = ({children}) => {
const active = content != setContent
if (active) {
return (
<Section>
{children}
</Section>
)
}
}
//Timeline section
<div>
<Bar1 onClick={() => setContent(BarContent_1)}/>
<Bar2 onClick={() => setContent(BarContent_2)}/>
</div>
//Content display and wrapping the content in my Refresh function
<Refresh>
{content}
</Refresh>
//Content of each bar, no need to wrap content in my styled (Section) tag since my Refresh button will
const BarContent_1 = () => {
return (
<div>
Content of bar 1
</div>
)
}
const BarContent_2 = () => {
return (
<div>
Content of bar 2
</div>
)
}

adding item in to the dom needs tow click rather than one in react?

I am using this below function (functional component with hooks) to add items in to the Dom.
I want to keep the fontawosome icon in the code, hidden and once the user clicks on a utility div, an icon must show in UI, after the icon shows, the user can add an item in to the movements div the problem is I have to double click all the time to add that mentioned item instead of clicking once!!
any idea what is the deal with double click ?
import React, {useState} from 'react'
import StabelItem from './StabelItem';
import { icon, library } from '#fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faPlusCircle} from '#fortawesome/free-solid-svg-icons';
library.add(faPlusCircle)
function addItems() {
// item component imported !!
let item = [<StabelItem />]
// Icon State !! local
const [icons, setIcon] = useState([]);
// Items State !! local
const [stabelItem, setStabelItem] = useState({items:[]});
// Show the icon so the use can add itmes +
const showIcon = ()=> {
// maipulate the state so the icon can show !!
setIcon(<FontAwesomeIcon onClick={() => {addStabelItem()}} icon={["fas", "plus-circle"]}/>)
}
// add Items !! when clicking on the icon this funtion is inside of showicon !!
const addStabelItem = () => {
setStabelItem({items: [...stabelItem.items, ...item]})
}
return (
<div className="utility" onClick={showIcon}>
<li className="box-item" >
<div className="flexrow" >
<div className='number norest'>Total</div>
<div className='number calc margin-left norest'>0</div>
</div>
<ul className="movements">
{/* itmes gets added!! when icon is getting clicked */}
{stabelItem.items}
{/* icons Show on demand */}
{icons}
</ul>
</li>
</div>
)
}
I think you should edit the function you pass to the icon like this:
setIcon(<FontAwesomeIcon onClick={addStabelItem} icon={["fas", "plus-circle"]}/>)
I had to change the structure making the icon on show, rather than making the state showing it, so ternary operator solved the issue.
function StabelBoxUtility() {
// item component
let item = [<StabelItem />]
// Icon State
const [iconsVisble, setIconVisble] = useState(false);
// Items State
const [stabelItem, setStabelItem] = useState({items:[]});
// show Icon
const showIcon = () => setIconVisble(true)
// add Items !! the icon must be on display
const addStabelItem = () => {
setStabelItem({items: [...stabelItem.items, ...item]})
}
return (
<div className="utility" onClick={showIcon}>
<li className="box-item" >
<div className="flexrow" >
<div className='number norest'>Total</div>
<div className='number calc margin-left norest'>0</div>
</div>
<ul className="movements" >
{stabelItem.items}
{iconsVisble ? <FontAwesomeIcon className="changable margin-left" onClick={addStabelItem} icon={["fas", "plus-circle"]} size="lg" />: null}
</ul>
</li>
</div>
)
}

Wanting to make an arrow animation to expand or collapse the link's sublinks list ReactJS

First time the function executes correctly, when clicking on button all times afterwards - no. I think it gets terminated somewhere.
I think that in some of the checkpoints the value for some reason is null, and maybe that is why it isn;t executing, ebacsie first time it always executes correctly, and all times afterwards - no. And all times afterwards I don't see the console log of "Function almost finished executing"
Expected behaviour is to have an icon arrow, onClick on which will open/close the main link's sublinks list in the navigation.
Like this:
icon is ">" and link is somelink
when somelink gets clicked, the appearance of navigation I want to be like: "<" somelink link1 link2 link3 (so that submenu open)
I'm using React hooks. Please help me with that, what am I doing wrong?
const ServiceConditions = () => {
const [arrowIsClicked, setArrowIsClicked] = React.useState(false);
const handleSublinksExpandCollapse = (e : React.MouseEvent<SVGSVGElement>) => {
console.log("arrow is clicked");
if (arrowIsClicked) return;
setArrowIsClicked(true);
if ((e.target as HTMLElement).style.transform === "rotate(0deg)") {
(e.target as HTMLElement).style.transform = "rotate(180deg)";
if (!e.target) console.log(e.target); return
const parent = (e.target as HTMLElement).parentNode;
if (!parent) console.log(e.target); return
const nextSibling = parent.nextSibling;
if (!nextSibling) console.log(e.target); return
(nextSibling as HTMLElement).style.display = "block";
console.log("function has almost done executing")
setArrowIsClicked(false)
} else { setArrowIsClicked(true); (e.target as HTMLElement).style.transform = "rotate(0deg)";
if (!e.target) console.log(e.target); return
const parent = (e.target as HTMLElement).parentNode;
if (!parent) console.log(e.target); return
const nextSibling = parent.nextSibling;
if (!nextSibling) console.log(e.target); return
(nextSibling as HTMLElement).style.display = "none"; setArrowIsClicked(false); }
}
return (
<Container>
{ isDesktop &&
<><NavigationContainer>
<StyledScrollSpy
scrollTargetIds={["section_1", "section_2", "section_3"]}
offset={100}
activeNavClass="is-active"
scrollDuration="1000"
headerBackground="true"
>
<List>
<MainRef><Line1><MainRefTypography variant="body1">Home</MainRefTypography><IconStyled id="ad-ap" onClick={(e : React.MouseEvent<SVGSVGElement>) => {handleSublinksExpandCollapse(e)}}></IconStyled></Line1><div><LinksInsideList>
<MainRef><LinkInsideTypography variant="body4">Section 1</LinkInsideTypography></MainRef>
<MainRef><LinkInsideTypography variant="body4">Section 2</LinkInsideTypography></MainRef>
<MainRef><LinkInsideTypography variant="body4">Section 3</LinkInsideTypography></MainRef>
</LinksInsideList></div></MainRef>
</List>
</StyledScrollSpy>
</NavigationContainer></> }
<Articles>
<PageName variant={isDesktop ? "h3" : "h1"}>SERVICE CONDITIONS</PageName>
<Bar align={BarAlign.Left} />
<Spacing mobile={3} desktop={3}/>
<div style={{"height": "400px", width: "100%"}}><span>Welcome!</span></div>
<div id="section_1" style={{"height": "500px", width: "100%"}}><span>Section 1</span></div>
<div id="section_2" style={{"height": "500px", width: "100%"}}><span>Section 2</span></div>
<div id="section_3" style={{"height": "500px", width: "100%"}}><span>Section 3</span></div>
</Articles>
</Container>
);
};
export default ServiceConditions;
Well, I think the problem is with this:
"if (arrowIsClicked) return;"
First it runs entirely setting arrowIsClicked to true, and afterwards at that line it exits every time.
Here is a simple way to implement a button that has the arrow rotating based on open/close state:
function CollapsibleButton() {
const [open, setOpen] = useState(false);
return (
<button
style={{
display: "inline-block",
}}
// Simply setting open to the opposite value.
onClick={() => setOpen(!open)}
>
<span
style={{
// This makes it feel animated:
transition: "transform 200ms linear",
// This rotates the element:
transform: `rotateZ(${open ? 0 : "180deg"})`,
display: "inline-block",
}}
>
{"<"}
</span>
Click me
</button>
);
}

Having an active state toggler on multiple buttons

I am trying to have my buttons light up once clicked, and dim when clicked twice. To indicate the user has already clicked that button.
const [isActive, setActive] = useState("false");
const handleToggle = () => {
setActive(!isActive);
};
And my buttons are toggling with the following code
<button
type="button"
value="Peperoni, "
className={isActive ? "button btn first" : "button btn firstActive"}
onClick={(event) => {
ToppingPlusMinus(event);
}}
>
Pepperoni
</button>
So far I have this working, however the buttons are all tied to the same state so when one is clicked they all light up. I am stumped on how to downsize this and make each button toggle its active class individually.
I'm aware I could just brute force it with many states for each button, but I know there is a smarter solution on there.
Thank you in advance for your time.
My full code is on a codesandbox here
You need to create a component that tracks its own state and fires the onClick
const ButtonClickable = props => {
const [isActive, setActive] = useState(false);
const handleToggle = (ev) => {
setActive(!isActive);
props.onClick && props.onClick(props.value)
};
return <button
type="button"
className={isActive ? "button btn first" : "button btn firstActive"}
onClick={handleToggle}
>
{props.value}
</button>
}
Then in the parent compoennt
<ButtonClickable onClick={ToppingPlusMinus} value="Peperoni, "/>
<ButtonClickable onClick={ToppingPlusMinus} value="Other, "/>
<ButtonClickable onClick={ToppingPlusMinus} value="Milk, "/>
Note that ToppingPlusMinus becomes ToppingPlusMinus(value)

Using variables to set attribute values in a tag

I am trying to use the vis variable in the display attribute as shown in the code. There are no errors in the console but the paragraph wont hide on pressing the button.
let vis = false;
const handleClick = () => {
vis = !vis;
render();
};
const render = () => {
const jsx = (
<div>
<h1>Toggle</h1>
<p display = {vis ? "block" : "none"} >Hi</p>
<button onClick = {handleClick}>{vis ? 'Hide details' : 'Show details'}</button>
</div>
);
ReactDOM.render(jsx, document.getElementById("app"));
}
render();
Here's a simple and working alternative:
let vis = false;
const handleClick = () => {
vis = !vis;
render();
};
const render = () => {
const jsx = (
<div>
<h1>Toggle</h1>
{vis ? <p>Hi</p> : ''}
<button onClick = {handleClick}>{vis ? 'Hide details' : 'Show details'}</button>
</div>
);
ReactDOM.render(jsx, document.getElementById("app"));
}
render();
The problem is I am not asking for other ways to do it. I just want to understand if I can use vis in display like I did in my first code snippet.
the paragraph wont hide on pressing the button.
It's the display property on the paragraph. This is a CSS property, not a stand-alone element attribute. That means it should go inside the style attribute, like this:
<p style={{display: vis ? "block" : "none"}} >Hi</p>
After making that one change, your code worked for me. Here's a working fiddle to demonstrate.
Is this what your are trying to achieve?
import React, { useState } from "react";
export default function App() {
const [toggle, setToggle] = useState(false);
return (
<div>
<button onClick={() => setToggle(!toggle)}>Toggle</button>
{toggle && <h1>Hello StackBlitz!</h1>}
<p>Start editing to see some magic happen :)</p>
</div>
);
}

Categories

Resources