REACT - przełącz klasę onclick

93

Próbuję dowiedzieć się, jak przełączyć aktywną klasę, onClickaby zmienić właściwości CSS.

Podjąłem wiele podejść i przeczytałem wiele odpowiedzi TAK. Korzystanie z jquery byłoby stosunkowo proste, jednak nie mogę wymyślić, jak to zrobić za pomocą reakcji. Mój kod jest poniżej. Czy ktoś może doradzić, jak mam to zrobić?

Czy można to zrobić bez tworzenia nowego komponentu dla każdego elementu?

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
źródło

Odpowiedzi:

86

Użyj stanu. Dokumenty React są tutaj .

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
źródło
3
czy można to zrobić bez tworzenia dodatkowego komponentu?
peter flanagan
2
Chcesz rozdzielić złożone komponenty na mniejsze komponenty, patrz facebook.github.io/react/docs/ ...
cssko
1
Zepsułem komponent, ale to nadal dodaje się activedo każdego elementu po kliknięciu. Chcę tylko jednego elementu, aby mieć aktywną klasę
peter flanagan
1
Nie można tego nazwać przełączaną „aktywną” klasą, ponieważ aktywne klasy innych elementów nadal istnieją.
Dat TT
6
czy możesz zaktualizować odpowiedź, aby używać onClick
zamiast
24

Wolałbym używać operatora "&&" w instrukcji wbudowanej if. Moim zdaniem w ten sposób zapewnia czystszą bazę kodu.

Generalnie możesz robić coś takiego

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

Pamiętaj tylko, że funkcja strzałki jest funkcją ES6 i pamiętaj, aby ustawić wartość 'this.state.active' w konstruktorze klasy () {}

this.state = { active: false }

lub jeśli chcesz wstrzyknąć css w JSX, możesz to zrobić w ten sposób

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

i możesz zadeklarować zmienną json stylu

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

pamiętaj o używaniu camelCase w arkuszach stylów JSX.

Jimi Pajala
źródło
2
To najsprytniejszy sposób przełączania prostych nazw klas komponentów.
SeaWarrior404
11

Twój addActiveClass musi wiedzieć, co zostało kliknięte. Coś takiego mogłoby zadziałać (zauważ, że dodałem informacje, które elementy div są aktywne jako tablica stanu, i że onClick przekazuje teraz informacje, które kliknięto jako parametr, po którym stan jest odpowiednio aktualizowany - z pewnością są sprytniejsze sposoby zrób to, ale masz pomysł).

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ć
źródło
9

Możesz to również zrobić za pomocą haczyków .

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

źródło
4

używając Reacta możesz dodać toggle class do dowolnego id / elementu, spróbuj

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

nazwa_pliku.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
źródło
Chciałbym dodać do tego, że możesz użyć następującego stylu kodu, jeśli masz klasę statyczną, której nie chcesz definiować dla każdej z instrukcji „isActive”:className={`left-panel ${this.state.isActive===true ? 'leftbar-open' : 'leftbar-close'}`}
Zeliax
4

React ma koncepcję stanu komponentów, więc jeśli chcesz to zmienić, zrób setState:

constructor(props) {
  super(props);

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

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

W swoim komponencie użyj this.state.isActivedo renderowania tego, czego potrzebujesz.

Staje się to bardziej skomplikowane, gdy chcesz ustawić stan w komponencie nr 1 i użyć go w komponencie nr 2. Po prostu przyjrzyj się bliżej jednokierunkowemu przepływowi danych i prawdopodobnie zmniejszeniu, które pomoże Ci sobie z tym poradzić.

dhorelik
źródło
Rozumiem, stateale to nadal nie wyjaśnia, jak zaktualizować nazwę klasy po kliknięciu elementu
peter flanagan
w swoich znacznikach jsx wykonaj - <div className={this.state.isActive ? 'active' : ''}>. Chodzi o renderowanie warunkowe, ale nadal opiera się na stanie.
dhorelik
ale to doda aktywną klasę do każdego elementu po kliknięciu. Chcę tylko, aby element, na który kliknięto, miał activenazwę klasy
peter flanagan
2
dlaczego każdy przedmiot? Doda klasę do elementu, dla którego dodajesz warunek. Idealnie byłoby, gdyby Twoje pozycje zostały wyodrębnione z Itemkomponentu. Następnie umieścisz go 3 razy i zasadniczo będziesz miał 3 elementy, z których każdy ma swój własny isActivestan. Czy to masz na myśli?
dhorelik
bez dzielenia go na Itemkomponent, czy jest na to sposób?
peter flanagan
2

Dobra próbka pomogłaby lepiej zrozumieć rzeczy:

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

Kod reakcji

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
źródło
2

możesz dodać klasę przełączania lub stan przełączania po kliknięciu

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
źródło
2

Dzięki @cssko za udzielenie poprawnej odpowiedzi, ale jeśli spróbujesz tego sam, zorientujesz się, że to nie działa. @Matei Radu zasugerował, ale został odrzucony przez @cssko, więc kod pozostaje nie do uruchomienia (wyświetli się błąd „Nie można odczytać powiązania właściwości z niezdefiniowanej”). Poniżej znajduje się działająca poprawna odpowiedź:

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
źródło
1

Niedawno zacząłem uczyć się Reacta i chciałem zbudować kartę, aby zobaczyć, jak daleko zaszła moja wiedza. Natknąłem się na to i postanowiłem wdrożyć coś bez redukcji. Wydaje mi się, że odpowiedzi nie odzwierciedlają tego, co OP chce osiągnąć. Chce tylko jednego aktywnego składnika, ale odpowiedzi tutaj spowodują, że wszystkie składniki będą aktywne. Dałem temu szansę.

Poniżej znajduje się plik karty

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;

Plik tabs ...

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;

twój plik indeksu ...

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


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

i css

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

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

To jest szkielet czegoś, co chcę ulepszyć, więc zwiększenie tabNumber powyżej 4 zepsuje css.

J.Ewa
źródło
1

Oto kod, który wymyśliłem:

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
źródło
1

React ma koncepcję stanu komponentów, więc jeśli chcesz to przełączyć, użyj 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. komponenty / 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. Wynik

wprowadź opis obrazu tutaj

Rahul Shukla
źródło
0

Chciałem tylko dodać moje podejście. Korzystanie z punktów zaczepienia i dostawcy kontekstu.

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
}

Działa szybko i zastępuje redux.

Aaron Tomlinson
źródło