REACT - basculer la classe en cliquant

93

J'essaie de comprendre comment basculer une classe active onClickpour modifier les propriétés CSS.

J'ai adopté de nombreuses approches et lu de nombreuses réponses SO. En utilisant jquery, ce serait relativement simple, cependant, je ne parviens pas à le faire avec react. Mon code est ci-dessous. Quelqu'un peut-il conseiller comment je dois faire cela?

Sans créer un nouveau composant pour chaque élément, est-il possible de faire cela?

class Test extends Component(){

    constructor(props) {

    super(props);
    this.addActiveClass= this.addActiveClass.bind(this);

  }

  addActiveClass() {

    //not sure what to do here

  }

    render() {
    <div>
      <div onClick={this.addActiveClass}>
        <p>1</p>
      </div>
      <div onClick={this.addActiveClass}>
        <p>2</p>
      </div>
        <div onClick={this.addActiveClass}>
        <p>3</p>
      </div>
    </div>
  }

}
Peter Flanagan
la source

Réponses:

86

Utiliser l'état. Les documents Reacts sont ici .

class MyComponent extends Component {
    constructor(props) {
        super(props);
        this.addActiveClass= this.addActiveClass.bind(this);
        this.state = {
            active: false,
        };
    }
    toggleClass() {
        const currentState = this.state.active;
        this.setState({ active: !currentState });
    };

    render() {
        return (
            <div 
                className={this.state.active ? 'your_className': null} 
                onClick={this.toggleClass} 
            >
                <p>{this.props.text}</p>
            </div>
        )
  }
}

class Test extends Component {
    render() {
        return (
            <div>
                <MyComponent text={'1'} />
                <MyComponent text={'2'} />
            </div>
        );
    }
}
cssko
la source
3
y a-t-il un moyen de faire cela sans créer de composant supplémentaire?
peter flanagan
2
Vous voulez séparer les composants complexes en composants plus petits voir facebook.github.io/react/docs/…
cssko
1
J'ai cassé le composant, mais cela ajoute toujours activeà chaque élément au clic. Je ne veux qu'un seul élément pour avoir une classe active
Peter Flanagan
1
Cela ne peut pas être appelé classe «active» basculée car les classes actives d'autres éléments sont toujours là.
Dat TT
6
pouvez-vous s'il vous plaît mettre à jour la réponse à utiliser onClickpar opposition à toutes les minuscules car vous avez incorrectement
peter flanagan
24

Je préférerais utiliser "&&" -operator sur l'instruction if en ligne. À mon avis, cela donne une base de code plus propre de cette façon.

En général, vous pourriez faire quelque chose comme ça

render(){
return(
  <div>
    <button className={this.state.active && 'active'}
      onClick={ () => this.setState({active: !this.state.active}) }>Click me</button>
  </div>
)
}

Gardez simplement à l'esprit que la fonction de flèche est une fonctionnalité ES6 et n'oubliez pas de définir la valeur 'this.state.active' dans la classe constructor () {}

this.state = { active: false }

ou si vous souhaitez injecter du CSS dans JSX, vous pouvez le faire de cette façon

<button style={this.state.active && style.button} >button</button>

et vous pouvez déclarer la variable json de style

const style = { button: { background:'red' } }

n'oubliez pas d'utiliser camelCase sur les feuilles de style JSX.

Jimi Pajala
la source
2
C'est le moyen le plus simple de faire basculer les noms de classe simples sur les composants.
SeaWarrior404
11

Eh bien, votre addActiveClass doit savoir ce qui a été cliqué. Quelque chose comme ça pourrait fonctionner (notez que j'ai ajouté les informations sur les divs actifs en tant que tableau d'état, et que onClick transmet maintenant les informations sur lesquelles on a cliqué en tant que paramètre, après quoi l'état est mis à jour en conséquence - il existe certainement des moyens plus intelligents de faites-le, mais vous avez l'idée).

class Test extends Component(){

  constructor(props) {
    super(props);
    this.state = {activeClasses: [false, false, false]};
    this.addActiveClass= this.addActiveClass.bind(this);
  }

  addActiveClass(index) {
    const activeClasses = [...this.state.activeClasses.slice(0, index), !this.state.activeClasses[index], this.state.activeClasses.slice(index + 1)].flat();
    this.setState({activeClasses});
  }

  render() {
    const activeClasses = this.state.activeClasses.slice();
    return (
      <div>
        <div className={activeClasses[0]? "active" : "inactive"} onClick={() => this.addActiveClass(0)}>
          <p>0</p>
        </div>
        <div className={activeClasses[1]? "active" : "inactive"} onClick={() => this.addActiveClass(1)}>
          <p>1</p>
        </div>
          <div  onClick={() => this.addActiveClass(2)}>
          <p>2</p>
        </div>
      </div>
    );
  }
}
Bane Bojanić
la source
9

Vous pouvez également le faire avec des crochets .

function MyComponent (props) {
  const [isActive, setActive] = useState(false);

  const toggleClass = () => {
    setActive(!isActive);
  };

  return (
    <div 
      className={isActive ? 'your_className': null} 
      onClick={toggleClass} 
    >
      <p>{props.text}</p>
    </div>
   );
}  

la source
4

en utilisant React, vous pouvez ajouter une classe à bascule à n'importe quel identifiant / élément, essayez

style.css

.hide-text{
    display: none !important;
    /* transition: 2s all ease-in 0.9s; */
  }
.left-menu-main-link{
    transition: all ease-in 0.4s;
}
.leftbar-open{
    width: 240px;
    min-width: 240px;
    /* transition: all ease-in 0.4s; */
}
.leftbar-close{
    width: 88px;
    min-width:88px;
    transition: all ease-in 0.4s;
}

fileName.js

......
    ToggleMenu=()=>{
             this.setState({
                isActive: !this.state.isActive
              })
              console.log(this.state.isActive)
        }
        render() {
            return (
                <div className={this.state.isActive===true ? "left-panel leftbar-open" : "left-panel leftbar-close"} id="leftPanel">
                    <div className="top-logo-container"  onClick={this.ToggleMenu}>
                            <span className={this.state.isActive===true ? "left-menu-main-link hide-from-menu" : "hide-text"}>Welcome!</span>
                    </div>

                    <div className="welcome-member">
                        <span className={this.state.isActive===true ? "left-menu-main-link hide-from-menu" : "hide-text"}>Welcome<br/>SDO Rizwan</span>
                    </div>
    )
    }
......
Rizo
la source
Je voudrais ajouter à cela que vous pouvez utiliser le style de code suivant si vous avez une classe statique que vous ne voulez pas définir pour chacune des instructions "isActive":className={`left-panel ${this.state.isActive===true ? 'leftbar-open' : 'leftbar-close'}`}
Zeliax
4

React a un concept d'état des composants, donc si vous voulez le changer, faites un setState:

constructor(props) {
  super(props);

  this.addActiveClass= this.addActiveClass.bind(this);
  this.state = {
    isActive: false
  }
}

addActiveClass() {
  this.setState({
    isActive: true
  })
}

Dans votre composant, utilisez this.state.isActivepour rendre ce dont vous avez besoin.

Cela devient plus compliqué lorsque vous souhaitez définir l'état dans le composant n ° 1 et l'utiliser dans le composant n ° 2. Il suffit de creuser davantage dans le flux de données unidirectionnel et éventuellement le redux qui vous aidera à le gérer.

Dhorelik
la source
Je comprends, statemais cela n'explique toujours pas vraiment comment mettre à jour le nom de la classe lorsqu'un élément est cliqué
peter flanagan
dans votre balisage jsx faire - <div className={this.state.isActive ? 'active' : ''}>. Il s'agit de rendu conditionnel, mais repose toujours sur l'état.
dhorelik
mais cela ajoutera la classe active à chaque élément au clic. Je veux juste que l'élément cliqué pour avoir le activenom de la classe
Peter Flanagan
2
pourquoi chaque article? Cela ajoutera la classe à l'élément pour lequel vous ajoutez la condition. Idéalement, vos éléments doivent être résumés au Itemcomposant. Vous l'incluerez ensuite 3 fois et aurez essentiellement 3 éléments, chacun ayant son propre isActiveétat. C'est ce que tu veux dire?
dhorelik
sans le diviser en un Itemcomposant, y a-t-il un moyen de le faire?
peter flanagan
2

Un bon échantillon aiderait à mieux comprendre les choses:

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; 
}

Code de réaction

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"));
Alex Jolig
la source
2

vous pouvez ajouter une classe de bascule ou un état de bascule en cliquant sur

class Test extends Component(){
  state={
  active:false, 
 }
  toggleClass() {
    console.log(this.state.active)
    this.setState=({
     active:true,
   })
  }

    render() {
    <div>
      <div onClick={this.toggleClass.bind(this)}>
        <p>1</p>
      </div>
    </div>
  }

}
Rizo
la source
2

Merci à @cssko pour avoir fourni la bonne réponse, mais si vous l'avez essayé vous-même, vous vous rendrez compte que cela ne fonctionne pas. Une suggestion a été faite par @Matei Radu, mais a été rejetée par @cssko, donc le code reste impossible (il lancera l'erreur 'Impossible de lire la liaison de propriété d'undefined'). Voici la bonne réponse:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.addActiveClass = this.addActiveClass.bind(this);
    this.state = {
      active: false,
    };
  }
  addActiveClass() {
    const currentState = this.state.active;
    this.setState({
      active: !currentState
    });
  };

  render() {
    return ( <
      div className = {
        this.state.active ? 'your_className' : null
      }
      onClick = {
        this.addActiveClass
      } >
      <
      p > {
        this.props.text
      } < /p> < /
      div >
    )
  }
}

class Test extends React.Component {
  render() {
    return ( <
      div >
      <
      MyComponent text = {
        'Clicking this will toggle the opacity through css class'
      }
      /> < /
      div >
    );
  }
}

ReactDOM.render( <
  Test / > ,
  document.body
);
.your_className {
  opacity: 0.3
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

Samson
la source
1

J'ai commencé à apprendre React récemment et je voulais créer un onglet juste pour voir jusqu'où mes connaissances sont allées. Je suis tombé sur cela et j'ai décidé de mettre en œuvre quelque chose sans redux. J'ai l'impression que les réponses ne reflètent pas ce que l'OP veut réaliser. Il ne veut qu'un seul composant actif, mais les réponses ici définiront tous les composants actifs. Je lui ai donné une chance.

Ci-dessous un fichier onglet

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;

Le fichier des onglets ...

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;

votre fichier d'index ...

import React from 'react';
import ReactDOM from 'react-dom';
import Tabs from './components/tabs';


ReactDOM.render(
    <Tabs />
  , document.querySelector('.container'));

et le css

.tab-bar {
    margin: 10px 10px;
    border: 1px solid grey;
}

.active-tab {
    border-top: 1px solid red;
}

Ceci est un squelette de quelque chose que je veux améliorer, donc augmenter le tabNumber au-delà de 4 cassera le css.

J.Ewa
la source
1

Voici un code que j'ai trouvé:

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>
        );
    };
};
Vadim Plisko
la source
1

React a un concept d'état des composants, donc si vous voulez basculer, utilisez setState:

  1. 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;
  1. composants / 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;
  1. Production

entrez la description de l'image ici

Rahul Shukla
la source
0

Je voulais juste ajouter mon approche. Utilisation de hooks et de fournisseur de contexte.

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
}

Fonctionne rapidement et remplace redux.

Aaron Tomlinson
la source