Removing class from last clicked event in React - css

I have a Navbar with tabs, and I want to make a function called onClick that gets the event, and adds a class to that tab "active". But, when I click another tab, it should remove a class from the previous tab and add it to that one.
Sample of my code:
const [clickedTab, setClickedTab] = useState(true);
function Click() {
if (clickedTab === true) {
console.log(clickedTab);
tab.classList.add("active");
}
else {
console.log("Error!");
}
}

In React use the model (via useState() in this case) to make changes to the view.
Set the activeId of the tab in the state, and if the tab's id is equal to activeId pass set it's active to true. The tab itself can add/remove the className.
const { useState } = React;
const Tab = ({ id, active, setActive }) => (
<li
className={`tab ${active ? 'active' : ''}`}
onClick={() => setActive(id)}
>
{id}
</li>
);
const Example = ({ tabs }) => {
const [activeId, setActive] = useState();
return (
<ul className="tabs">
{tabs.map(id => (
<Tab
key={id}
id={id}
setActive={setActive}
active={id === activeId}
/>
))}
</ul>
);
}
const tabs = [1, 2, 3, 4];
ReactDOM.render(
<Example tabs={tabs} />,
root
);
.tabs {
display: flex;
list-style: none;
}
.tab {
height: 1em;
width: 2em;
border: 1px solid red;
text-align: center;
cursor: pointer;
}
.active {
background: red;
}
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

Here is a simple example so you can get the idea; we hold current active element id in a state then based on that we decide what class name it should have; no need to use classList's add or remove
function Header () {
const [active, setActive] = React.useState();
return (
<header>
<ul>
<li
className={active === 1? "red":"blue"}
onClick={()=>setActive(1)}
>a</li>
<li
className={active === 2? "red":"blue"}
onClick={()=>setActive(2)}
>b</li>
<li
className={active === 3? "red":"blue"}
onClick={()=>setActive(3)}
>c</li>
</ul>
</header>
);
}
ReactDOM.render( <Header />, app );
.red{background:red}.blue{background:blue}li{color:#fff;width:30px}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>

Related

How to add a class on click, and remove the same class from all other elements?

I have a button navigation and when you click on a button, the active class is added. My goal is for the active class to be added to the button clicked, but remove that class of active on all other buttons if present. The 'About' button will have a class of active on page load.
Not sure how to translate this to React, in JavaScript on click I would remove the class from all the elements in a loop and add a class to the target clicked if it did not already have the active class.
Code Sandbox - https://codesandbox.io/s/toggle-active-on-class-clicked-remove-from-the-rest-r467l1?file=/src/App.js
export default function Header() {
const [active, setActive] = useState(true);
const toggleColor = function (e) {
// on load, 'About' button has active class
// when clicking another menu item add active class, remove active from the rest of buttons
console.log(e.target);
};
return (
<header className="header-img-container">
<nav>
<ul>
<li>
<button onClick={toggleColor} className={active ? "active" : ""}>
About
</button>
</li>
<li>
<button onClick={toggleColor}>Skills</button>
</li>
<li>
<button onClick={toggleColor}>Projects</button>
</li>
<li>
<button onClick={toggleColor}>Words</button>
</li>
</ul>
</nav>
</header>
);
}
There are so many ways to solve that problem. You can try this if it's meet your requirements.
import "./styles.css";
import { useState } from "react";
const list = ["About", "Skills", "Projects", "Words"];
export default function Header() {
const [activeLink, setActiveLink] = useState("About");
return (
<header className="header-img-container">
<nav>
<ul>
{list.map((item) => (
<li key={item}>
<button
onClick={() => setActiveLink(item)}
className={activeLink === item ? "active" : ""}
>
{item}
</button>
</li>
))}
</ul>
</nav>
</header>
);
}
Create a state like this
const [active, setActive] = useState({About: true, Skills: false, Projects: false, Words: false})
А change local parameter to add a class to element. For example
<li>
<button onClick={() => {
setActive({...active, About: false, Skills: true, Projects: false,
Words: false })
}}>Skills</button>
</li>
There are many possible approaches, here is a basic example that uses an object type active state to store the value for each list item.
const [active, setActive] = useState({ About: true })
The list data is stored in an array so it can be mapped in the JSX part of the component.
const itemList = ["About", "Skills", "Projects", "Words"]
While index is not an ideal key it is used here just for example purpose.
{
itemList.map((item, index) => (
<li key={index}>
<button
onClick={() => toggleColor(item)}
className={active[item] ? "active" : ""}
>
{item}
</button>
</li>
));
}
toggleColor sets value for active, and it specify that active should always be in the format of {About: true}, {Skills: true} and such. The !!! covers the case when certain keys are not existing in the object.
const toggleColor = function (item) {
setActive((prev) => {
return { [item]: !!!prev[item] };
});
};
Below is the full example, it runs in the snippet for convenience.
function Header() {
const [active, setActive] = React.useState({ About: true });
const itemList = ["About", "Skills", "Projects", "Words"];
const toggleColor = function (item) {
// on load, 'About' button has active class
// when clicking another menu item add active class, remove active from the rest of buttons
setActive((prev) => {
return { [item]: !!!prev[item] };
});
};
return (
<header className="header-img-container">
<nav>
<ul>
{itemList.map((item, index) => (
<li key={index}>
<button
onClick={() => toggleColor(item)}
className={active[item] ? "active" : ""}
>
{item}
</button>
</li>
))}
</ul>
</nav>
</header>
);
}
const App = () => {
return (
<div>
<Header />
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#root"));
.App {
font-family: sans-serif;
text-align: center;
}
button {
padding: 6px;
}
.active {
border: 1px solid pink;
color: hotpink;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.production.min.js"></script>

Using localStorage to keep css style in react after page reload

I would like to use localStorage to store a css style :focus for my elements. Currently when I click on component in my Dropdown it toggles green color, but when I click outside of it or I reload page, it disappears. Check the screenshot below.
How I can implement here localStorage to save :focus after page reload or maybe there is even better way of doing that which I don't know?
My Menu
const DropdownMenu = (props) => {
let companyElements = props.state.companies.map(c => (
<DropdownItem key={c.id} name={c.name} owner={c.owner} id={c.id} /> ));
return (
<div className="dropdown">
{companyElements}
</div>
);
}
My Item
const NavItem = (props) => {
const [open, setOpen] = useState(false);
return (
<div>
<li className="nav-item">
<a href="#" className="icon-button"
onClick={() => setOpen(!open)}>
{props.state.companies[0].name}
</a>
{open && props.children}
</li>
</div>
);
}
Style
.menu-item:focus{
background-color: green;
}

How to change style of an imported component?

I'm importing this component into NewComponent.jsx
import OptionsMenu from "../../components/OptionsMenu/OptionsMenu";
where it's rendered as such:
<OptionsMenu
options={[
{
icon: "trash-alt",
action: () => this.toggleDeleteModal(index),
title: "Delete",
},
]}
/>
The options menu component uses a file called styles.js which has a colour set too:
export const OptionsMenuDropDown = styled.div`
position: absolute;
top: -40px;
left: -200%;
color: #0F4379;
So it's usage would be
<OptionsMenuDropDown>
<ol>
{options.map((option, index) => {
return (
<li>
<button className="options-btn" key={index} onClick={option.action}>
<p >{option.title}</p>
</button>
</li>
);
})}
</ol>
</OptionsMenuDropDown>
Is it possible to change this colour when its being used in NewComponent.jsx ? So that I can override it with another colour?
I cannot use a separate .css file for NewComponent.jsx but can use a styles.js. There is also a global css file too
You can pass a prop into your styled component, like the color.
const OptionsMenu = ({color}) => {
return (
<OptionsMenuDropDown color="color">
<ol>
{options.map((option, index) => {
return (
<li>
<button className="options-btn" key={index} onClick={option.action}>
<p >{option.title}</p>
</button>
</li>
);
})}
</ol>
</OptionsMenuDropDown>
)
}
Then in your styles.js.
export const OptionsMenuDropDown = styled.div`
position: absolute;
top: -40px;
left: -200%;
color: ${props => props.color};
Then you can use it in you NewComponent passing another color as a prop.
const NewComponent = () => {
<OptionsMenu color="#fff" />
}

How to add style on a particular image when hovering in ReactJS?

The problem is that I wanted to add a border to the image and display a Card that contains information when hovering but instead it displays to all the images.
After fetching data from a movie API, I stored the response in the state:
const [movies, setMovies] = useState([])
useEffect(() => {
/**side-effects**/
setMovies(response.data)
},[])
then
let style = {
display: 'none'
};
let imgStyle = {};
if(hover){
style = {
display: 'block'
}
imgStyle = {
border: 1px solid white
}
}
if(movies.length > 1) {
display = (
<div className="MovieContainer">
<h2>Trending this week</h2>
<Carousel>
{movies.map((movie) => {
return (
<>
<img
style={imgStyle}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
className="Images"
key={movie.id}
src={movie.image}
alt={movie.title}
/>
<Card style={style}>{movie.title}</Card>
</>
);
})}
</Carousel>
</div>
);
}
but instead of adding a style to the *hovered* image, it also adds the style to all images.
I also tried using css for the border and it works the way I wanted but I couldn't think of any way for the Cards to show up when hovering
Why do not you do this modification directly in css file, you can just add to your css
img:hover { "2px solid black" }
instead of doing inline styling and using onMouseEnter onMouseLeave

React in CSS with react in viewport

I am using react in viewport from here: https://github.com/roderickhsiao/react-in-viewport and I set up the boiler plate so that the following line of code works:
<ViewportBlock onEnterViewport={() => console.log('enter')} onLeaveViewport={() => console.log('leave')} />
Looking at console.log it is saying enter and leave where I need it too. However, I need to have it say onEnterViewport set .Header (the css className is Header) to display:none in css, and onLeaveViewport set to display:block
Edit:
Full code:
const Block = (props: { inViewport: boolean }) => {
const { inViewport, forwardedRef } = props;
const color = inViewport ? '#217ac0' : '#ff9800';
const text = inViewport ? 'In viewport' : 'Not in viewport';
return (
<div className="viewport-block" ref={forwardedRef}>
{/* <h3>{ text }</h3>
<div style={{ width: '400px', height: '300px', background: color }} /> */}
<Link to="Header" spy={true} smooth={true} offset={-100} duration={1400}><img src={arrow} alt="arrow" className={inViewport ? 'hide' : 'Header-div2-mainnav-arrow' } /></Link>
</div>
);
};
const ViewportBlock = handleViewport(Block, /** options: {}, config: {} **/);
export const Header = () => ({
componentDidMount: function() {
Events.scrollEvent.register('begin', function(to, element) {
console.log('begin', arguments);
});
Events.scrollEvent.register('end', function(to, element) {
console.log('end', arguments);
});
scrollSpy.update();
},
componentWillUnmount: function() {
Events.scrollEvent.remove('begin');
Events.scrollEvent.remove('end');
},
scrollToBottom: function() {
scroll.scrollToBottom();
},
handleSetActive: function(to) {
console.log(to);
},
render: function() {
return (
<div className="Header">
<div className="Header-div1">
{/* background image */}
<h1 className="Header-div1-number">910-910-910</h1>
<h2 className="Header-div1-email">larryslawn#gmail.com</h2>
</div>
<div className="Header-div2">
<h1 className="Header-div2-h1"><span className="Header-div2-span">Larry's </span>Lawn Mowing</h1>
<p className="Header-div2-p">No job too big or too small, we do it all </p>
<div className="Header-div2-mainnav">
<Link to="Pricing" spy={true} smooth={true} offset={-50} duration={1200}><p>Pricing</p></Link>
<Link to="Services" spy={true} smooth={true} offset={-100} duration={1200}><p className="Header-div2-mainnav-p">Services</p></Link>
<Link to="Contact" spy={true} smooth={true} offset={-100} duration={1400}><p>Contact</p></Link>
</div>
<Block />
</div>
</div>
)
}
})
Use useState to toggle a class with display: none on the Header component:
const Example = () => {
const [inView, setInView] = useState(false)
return (
<>
<ViewportBlock
onEnterViewport={() => setInView(true)}
onLeaveViewport={() => setInView(false)}
/>
<Header className={inView ? 'hide' : '' }>Header</Header>
</>
)
}
CSS:
hide { display: none; }

Resources