I am trying to add side toggle menu box in main page which is positioning on right side of document when the button is clicked.
I made a function for toggling action(you can check the function below) with some of react hooks and added by using onClick() method. I clicked the btn to check if it works, but it doesn't. I changed onClick() method to onMouseEnter() and it worked. I added callback(onClick(()=>{function()})) but it still doesn't work.
I think toggling function doesn't have any problem(because it worked properly when it's on onMouseEnter). Some mechanisms of them makes the difference. I checked the docs of javascript but it was not helpful.
I wish somebody provide me a demonstration of this.
Here is my codes.
import React, { useState } from "react";
import "../css/side-toggle.css";
import hbgBtn from "../image/hamburgerBtn.png";
const SideBar = ({ wid = 380, children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggleMenu = () => {
setIsOpen((isOpen) => !isOpen);
console.log('1')
};
const closeMenu = () => {
setIsOpen((isOpen) => !isOpen);
}
return (
<div className="container" wid={wid}>
<img onMouseEnter={(e) => toggleMenu(e)}
className={!isOpen ? "show-btn" : "hide"}
src={hbgBtn}
alt=""
/>
<div onMouseEnter={closeMenu} className={isOpen? "dimmer" : 'hide'}>{children}</div>
<div className={isOpen ? "side-column" : "hide"}></div>
</div>
);
};
export default SideBar;
(and i will appreciate when you understand my wierd English. I'm ESL)
There is a possiblity that you may have made some error while styling. I have tried my best to replicate your code and it works for me fine.
import React, { useState } from 'react';
import './style.css';
const SideBar = ({ wid = 380, children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggleMenu = () => {
setIsOpen((isOpen) => !isOpen);
console.log('1');
};
const closeMenu = () => {
setIsOpen((isOpen) => !isOpen);
};
return (
<div className="container">
<div onClick={(e) => toggleMenu(e)}>
<img
className={!isOpen ? 'show-btn' : 'hide'}
src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Hamburger_icon.svg/1024px-Hamburger_icon.svg.png"
alt=""
/>
</div>
<div onMouseEnter={closeMenu} className={isOpen ? 'dimmer' : 'hide'}>
{children}
</div>
<div className={isOpen ? 'side-column' : 'hide'}></div>
</div>
);
};
export default SideBar;
Here is the stackblitz code link
https://stackblitz.com/edit/react-xtwxzc?file=src/App.js
Related
I'm building a simple dropdown with react and functional components. On strange behavior, I've run into is the way we have to think about conjures and state. This is a simplified version of my component:
export default function App() {
const [show, setShow] = useState(false);
const selectElement = useRef(null);
const handleToggle = (e) => {
if (selectElement) {
if (!selectElement.current.contains(e.target)) {
setShow(!show);
}
}
};
useEffect(() => {
document.addEventListener("click", handleToggle, false);
return () => document.removeEventListener("click", handleToggle, false);
}, []);
return (
<div className="App">
<div ref={selectElement} className="comp">
<h1 onClick={() => setShow(!show)}>Select</h1>
{show && (
<div>
<div>Inner 1</div>
<div>Inner 2</div>
</div>
)}
</div>
</div>
);
}
This component behaves wrong and it's not possible to toggle the dropdown correctly. The effect handler is registered on the first render and encloses the state of the first render (if I'm not wrong here). The registered function will not receive state updates. This is causing the error.
I'm not really sure what's the best way to fix this. Currently, I decided to simply remove the dependency array from the useEffect hook so that the effect handler is created and destroyed on every render/cleanup.
I've also created a Sandbox so my issue becomes more tangible.
I think this code will help you to solve your problem.
export default function App() {
const [show, setShow] = useState(false);
const selectElement = useRef(null);
const handleToggle = (e) => {
if (selectElement) {
if (!selectElement.current.contains(e.target)) {
setShow(false);
document.removeEventListener("click", handleToggle, false);
}
}
};
const handleClick = (e) => {
setShow(true)
document.addEventListener("click", handleToggle, false);
};
return (
<div className="App">
<div ref={selectElement} className="comp">
<h1 onClick={handleClick}>Select</h1>
{show && (
<div>
<div>Inner 1</div>
<div>Inner 2</div>
</div>
)}
</div>
</div>
);
}
I am looking to be able to update the size of .swiper-wrapper when there is a DOM event on the current slide. The method swiper.updateSize()/swiper.update() is firing after the DOM event but it is returning undefined. This is odd because when I console.log(swiper) (the instance of swiper) it returns the swiper object instance as expected. I have added the prop autoHeight to so according to the documentation, the height of the .swiper-wrapper should increase/decrease depending on the height of the current slide after the swiper.updateSize()/swiper.update() method is called
Is there something I am doing wrong here?
import React, { useState } from 'react';
function ParentComponent(props) {
let [swiper, setSwiper] = useState(null)
return(
<Swiper
onSwiper={(swiper) => {setSwiper(swiper)}}
autoHeight
>
<ChildComponent
title={"title"}
text={"text"}
swiper={swiper}
/>
</Swiper>
)
}
function ChildComponent(props) {
let [active, setActive] = useState(false)
let swiper = props.swiper
// toggle active class
const handleClick = () => {
if (active) {
setActive(false)
}
else {
setActive(true)
// returns swiper instance
console.log(swiper)
// returns undefined
swiper.updateSize();
}
}
return (
<div className={"infos" + (active ? ' active' : '')}>
<div onClick={handleClick}>
<h4>{props.title}<span></span></h4>
</div>
<div className="text">
<p>{props.text}</p>
</div>
</div>
)
}
export default ChildComponent
ok managed to fix this by setting a timeout and waiting until after the transition had finished (or roughly close to it). The issue seemed to be that the max-height transition hadn't finished by the time the update() method was called on swiper. I also used a different method called .updateAutoHeight() which worked
my code now looks like this:
import React, { useState } from 'react';
function ParentComponent(props) {
let [swiper, setSwiper] = useState(null)
return(
<Swiper
onSwiper={(swiper) => {setSwiper(swiper)}}
autoHeight
>
<ChildComponent
title={"title"}
text={"text"}
swiper={swiper}
/>
</Swiper>
)
}
function ChildComponent(props) {
let [active, setActive] = useState(false)
let swiper = props.swiper
// toggle active class
const handleClick = () => {
if (active) {
setActive(false)
setTimeout(() => {
swiper.updateAutoHeight()
}, 200)
}
else {
setActive(true)
setTimeout(() => {
swiper.updateAutoHeight()
}, 250)
}
}
return (
<div className={"infos" + (active ? ' active' : '')}>
<div onClick={handleClick}>
<h4>{props.title}<span></span></h4>
</div>
<div className="text">
<p>{props.text}</p>
</div>
</div>
)
}
export default ChildComponent
I would suggest changing the timeout ms depending on how long the transition takes to finish. The higher the height of the max-height transition, the longer it will take and therefore the more delay you will need to use on the setTimeout ms for it to be picked up by the JS
All methods of swiper need to be called in setTimeout function
I was wondering if there is a way to make it work. You can check the snippet that I added below.
import React, { useState } from "react";
import Item from "./Item";
export default function App() {
const [hoveredItem, setHoveredItem] = useState(null);
const items = [...Array(5000).keys()];
const handleMouseEnter = (item) => {
setHoveredItem(item);
};
return (
<div className="App">
{items.map((item) => (
<Item
key={item}
isHover={hoveredItem === item}
onMouseEnter={() => handleMouseEnter(item)}
>
Item {item}
</Item>
))}
</div>
);
}
The code above is just a snippet, using the pseudo class (:hover) wont work for what I need. The id of the item hovered needs to be in the state.
Here is a codesandbox link if you want to play around: https://codesandbox.io/s/floral-cache-wfxws
I do it using hooks:
const [hover, setHover] = useState(false)
return (
<div>
<div
onMouseOver={()=>setHover(true)}
onMouseOut={()=>setHover(false)}
className={" " + (hover ? "my-css-class" : "") }>
My text
</div>
</div>
)
Here's what I've tried doing
import React, { Fragment, useState } from "react";
const Accordion = ({ items }) => {
const [activeIndex, setActiveIndex] = useState(null);
const [display, setDisplay] = useState("");
const handleChange = (index) => {
setActiveIndex(index);
setDisplay(display === "active" ? "" : "active");
};
const renderedItems = items.map((item, index) => {
return (
<Fragment key={item.title}>
<div className={`${display} title`} onClick={() => handleChange(index)}>
<i className="dropdown icon"></i>
{item.title}
</div>
<div className={`${display} content`}>
<p>{item.content}</p>
</div>
</Fragment>
);
});
return (
<div className="ui styled accordion">
{renderedItems}
{activeIndex}
</div>
);
};
export default Accordion;
But on clicking a single item, it expands or collapses all others. Also, I want only one expanded item at any given time, so if a user clicks another item the previous one should close automatically.
Here is my App.js
import React from "react";
import Accordion from "./components/Accordion";
const items = [
{
title: "What is React?",
content: "React is a front end javascript library",
},
{
title: "What is Angular?",
content: "Angular is a front end javascript framework",
},
{
title: "What is Vue?",
content: "Vue is a front end javascript library",
},
];
const App = () => {
return (
<div>
<Accordion items={items} />
</div>
);
};
export default App;
For your issue :
// Means if display is active so nothing else it's active, so everything is active
setDisplay(display === "active" ? "" : "active");
The display variable is shared between each accordion bodies... Always the same
Do you really need an display var ?
import React, { Fragment, useState } from "react";
const Accordion = ({ items }) => {
const [activeIndex, setActiveIndex] = useState(null);
const handleChange = index => {
setActiveIndex(activeIndex === index ? null : index);
};
const renderedItems = items.map((item, index) => {
return (
<Fragment key={item.title}>
<div className={`${display} title`} onClick={() => handleChange(index)}>
<i className="dropdown icon"></i>
{item.title}
</div>
<div className={`${activeIndex === index ? 'active' : ''} content`}>
<p>{item.content}</p>
</div>
</Fragment>
);
});
return (
<div className="ui styled accordion">
{renderedItems}
{activeIndex}
</div>
);
};
export default Accordion;
The "Bad button" only works once, since it's not controlling the same "value" as the "Good button". What am I missing here?
const Test = () => {
const [value, setValue] = useState(0)
const [view, setView] = useState()
const add = () => setValue(value + 1)
const badButton = () => {
return (
<div>
<button onClick={add}>Bad button</button>
</div>
)
}
return (
<div>
{value}
<button onClick={add}>Good button</button>
{view}
<button onClick={() => setView(badButton)}>show bad button</button>
</div>
)
}
Thanks for replying, I'm going to use the flag method as suggested. But I would still like to know why the two buttons don't work the same way in this original case.
Check this sandbox
I feel it's the right way to handle such a usecase. Instead of storing component itself in state. I replaced it with a boolean. By default it will be false and badButton will be hidden, on click of showBadButton, i'm setting the view state true and bad button will come into picture. Actually Its a good buton now. Check it out.
I would use view as a flag to show/hide BadButton component, I have created a demo that showcase the following code snippet:
import React, { useState } from 'react';
import { render } from 'react-dom';
import './style.css';
const Test = () => {
const [value, setValue] = useState(0)
const [view, setView] = useState(false)
const add = () => setValue(value + 1)
const BadButton = () => {
return (
<button onClick={add}>Bad button</button>
)
}
return (
<>
{value}
<button onClick={add}>Good button</button>
{view ? BadButton() : null}
<button onClick={() => setView(!view)}>
{view ? 'hide' : 'show'} bad button
</button>
</>
)
}
render(<Test />, document.getElementById('root'));
Welcome to StackOverflow