I am trying to figure out how to toggle an active class onClick
to change CSS properties.
I have taken many approaches, and read many SO answers. Using
React has a concept of components state, so if you want to Toggle, use setState:
- App.js
import React from 'react';
import TestState from './components/TestState';
class App extends React.Component {
render() {
return (
<div className="App">
<h1>React State Example</h1>
<TestState/>
</div>
);
}
}
export default App;
- components/TestState.js
import React from 'react';
class TestState extends React.Component
{
constructor()
{
super();
this.state = {
message: 'Please subscribe',
status: "Subscribe"
}
}
changeMessage()
{
if (this.state.status === 'Subscribe')
{
this.setState({message : 'Thank You For Scubscribing.', status: 'Unsubscribe'})
}
else
{
this.setState({ message: 'Please subscribe', status: 'Subscribe' })
}
}
render()
{
return (
<div>
<h1>{this.state.message}</h1>
<button onClick={()=> this.changeMessage() } >{this.state.status}</button>
</div>
)
}
}
export default TestState;
- Output
Just wanted to add my approach. Using hooks and context provider.
Nav.js
function NavBar() {
const filterDispatch = useDispatchFilter()
const {filter} = useStateFilter()
const activeRef = useRef(null)
const completeRef = useRef(null)
const cancelRef = useRef(null)
useEffect(() => {
let activeClass = '';
let completeClass = '';
let cancelClass = '';
if(filter === ACTIVE_ORDERS){
activeClass='is-active'
}else if ( filter === COMPLETE_ORDERS ){
completeClass='is-active'
}else if(filter === CANCEL_ORDERS ) {
cancelClass='is-active'
}
activeRef.current.className = activeClass
completeRef.current.className = completeClass
cancelRef.current.className = cancelClass
}, [filter])
return (
<div className="tabs is-centered">
<ul>
<li ref={activeRef}>
<button
className="button-base"
onClick={() => filterDispatch({type: 'FILTER_ACTIVE'})}
>
Active
</button>
</li>
<li ref={completeRef}>
<button
className="button-base"
onClick={() => filterDispatch({type: 'FILTER_COMPLETE'})}
>
Complete
</button>
</li>
<li ref={cancelRef}>
<button
className={'button-base'}
onClick={() => filterDispatch({type: 'FILTER_CANCEL'})}
>
Cancel
</button>
</li>
</ul>
</div>
)
}
export default NavBar
filterContext.js
export const ACTIVE_ORDERS = [
"pending",
"assigned",
"pickup",
"warning",
"arrived",
]
export const COMPLETE_ORDERS = ["complete"]
export const CANCEL_ORDERS = ["cancel"]
const FilterStateContext = createContext()
const FilterDispatchContext = createContext()
export const FilterProvider = ({ children }) => {
const [state, dispatch] = useReducer(FilterReducer, { filter: ACTIVE_ORDERS })
return (
<FilterStateContext.Provider value={state}>
<FilterDispatchContext.Provider value={dispatch}>
{children}
</FilterDispatchContext.Provider>
</FilterStateContext.Provider>
)
}
export const useStateFilter = () => {
const context = useContext(FilterStateContext)
if (context === undefined) {
throw new Error("place useStateMap within FilterProvider")
}
return context
}
export const useDispatchFilter = () => {
const context = useContext(FilterDispatchContext)
if (context === undefined) {
throw new Error("place useDispatchMap within FilterProvider")
}
return context
}
export const FilterReducer = (state, action) => {
switch (action.type) {
case "FILTER_ACTIVE":
return {
...state,
filter: ACTIVE_ORDERS,
}
case "FILTER_COMPLETE":
return {
...state,
filter: COMPLETE_ORDERS,
}
case "FILTER_CANCEL":
return {
...state,
filter: CANCEL_ORDERS,
}
}
return state
}
Works fast, and replaces redux.
A good sample would help to understand things better:
HTML
<div id="root">
</div>
CSS
.box {
display: block;
width: 200px;
height: 200px;
background-color: gray;
color: white;
text-align: center;
vertical-align: middle;
cursor: pointer;
}
.box.green {
background-color: green;
}
React code
class App extends React.Component {
constructor(props) {
super(props);
this.state = {addClass: false}
}
toggle() {
this.setState({addClass: !this.state.addClass});
}
render() {
let boxClass = ["box"];
if(this.state.addClass) {
boxClass.push('green');
}
return(
<div className={boxClass.join(' ')} onClick={this.toggle.bind(this)}>{this.state.addClass ? "Remove a class" : "Add a class (click the box)"}<br />Read the tutorial <a href="http://www.automationfuel.com" target="_blank">here</a>.</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
I started learning React recently and wanted to build a tab just to see how far my knowledge has gone. I came across this and decided to implement something without redux. I kind of feel the answers don't reflect what op wants to achieve. He wants only one active component but the answers here will set all components active. I have given it a shot.
Below is a tab file
import React, { Component } from 'react';
class Tab extends Component {
render(){
const tabClassName = "col-xs-3 tab-bar";
const activeTab = this.props.activeKey === this.props.keyNumber ? "active-tab" : null;
return (
<div
className = {`${tabClassName} ${activeTab}`}
onClick={()=>this.props.onClick(this.props.keyNumber)}
>
I am here
</div>
);
}
}
export default Tab;
The tabs file...
import React, { Component } from 'react';
import Tab from './tab';
class Tabs extends Component {
constructor(props){
super(props);
this.state = {
currentActiveKey: 0,
tabNumber: 2
};
this.setActive = this.setActive.bind(this);
this.setTabNumber = this.setTabNumber.bind(this);
}
setTabNumber(number){
this.setState({
tabNumber: number
});
}
setActive (key){
this.setState({
currentActiveKey: key
});
}
render(){
let tabs = [];
for(let i = 0; i <= this.state.tabNumber; i++){
let tab = <Tab key={i} keyNumber={i} onClick={this.setActive} activeKey={this.state.currentActiveKey}/>;
tabs.push(tab);
}
return (
<div className="row">
{tabs}
</div>
);
}
}
export default Tabs;
your index file...
import React from 'react';
import ReactDOM from 'react-dom';
import Tabs from './components/tabs';
ReactDOM.render(
<Tabs />
, document.querySelector('.container'));
and the css
.tab-bar {
margin: 10px 10px;
border: 1px solid grey;
}
.active-tab {
border-top: 1px solid red;
}
This is a skeleton of something I want to improve on so increasing the tabNumber beyond 4 will break the css.
Here is a code I came Up with:
import React, {Component} from "react";
import './header.css'
export default class Header extends Component{
state = {
active : false
};
toggleMenuSwitch = () => {
this.setState((state)=>{
return{
active: !state.active
}
})
};
render() {
//destructuring
const {active} = this.state;
let className = 'toggle__sidebar';
if(active){
className += ' active';
}
return(
<header className="header">
<div className="header__wrapper">
<div className="header__cell header__cell--logo opened">
<a href="#" className="logo">
<img src="https://www.nrgcrm.olezzek.id.lv/images/logo.svg" alt=""/>
</a>
<a href="#" className={className}
onClick={ this.toggleMenuSwitch }
data-toggle="sidebar">
<i></i>
</a>
</div>
<div className="header__cell">
</div>
</div>
</header>
);
};
};
React has a concept of components state, so if you want to switch it, do a setState
:
constructor(props) {
super(props);
this.addActiveClass= this.addActiveClass.bind(this);
this.state = {
isActive: false
}
}
addActiveClass() {
this.setState({
isActive: true
})
}
In your component use this.state.isActive
to render what you need.
This gets more complicated when you want to set state in component#1 and use it in component#2. Just dig more into react unidirectional data flow and possibly redux that will help you handle it.