Як перемикати логічний стан компонента реакції?


85

Я хотів би знати, як перемикати логічний стан компонента реакції. Наприклад:

У мене є булева перевірка стану в конструкторі мого компонента:

constructor(props, context) { 
   super(props, context);

   this.state = {
      check: false
   };
};

Я намагаюся перемикати стан кожного разу, коли натискається мій прапорець, використовуючи метод this.setState:

<label><input type=checkbox" value="check" onChange = {(e) => this.setState({check: !check.value})}/> Checkbox </label>

Звичайно, я отримую Uncaught ReferenceError: перевірка не визначена . То як я можу цього досягти?

Заздалегідь велике спасибі.


3
Це саме так, як сказано, перевірка не визначена. Ви , ймовірно , мав в виду , щоб написати this.state.checkв this.setState({check: !check.value}). І додайте властивість, для якої встановлено прапорець, яка змінюватиметься залежно від стану компонента. checked={this.state.checked}
Вінкас Стоніс,

Відповіді:


247

Оскільки цього ніхто не розміщував, я публікую правильну відповідь. Якщо оновлення нового стану залежить від попереднього стану, завжди використовуйте функціональну форму, setStateяка приймає як аргумент функцію, яка повертає новий стан.

У вашому випадку:

this.setState(prevState => ({
  check: !prevState.check
}));

Див. Документи


Оскільки ця відповідь стає популярною, додавши підхід, який слід використовувати для React Hooks (v16.8 +):

Якщо ви використовуєте useStateхук, використовуйте такий код (якщо ваш новий стан залежить від попереднього стану):

const [check, setCheck] = useState(false);
// ...
setCheck(prevCheck => prevCheck + 1);

4
чому це краще, ніж безпосереднє використання this.setState({check: !this.state.check})?
SunnyPro

10
@SunnyPro Прочитайте пов'язані документи, і ви знайдете свою відповідь. TL; DR is response оптимізує виклики для встановлення стану шляхом їх групування. Тож уявіть просту функцію збільшення increment = () => this.setState({count: this.state.count + 1});та блок коду: increment(); increment(); increment();Тепер реакція може перетворити їх на щось аналогічне: setNewState = (oldState) => { newState.count = oldState.count + 1; newState.count = oldState.count + 1; newState.count = oldState.count + 1; return newState; } Подивіться, де криється проблема?
Drew Reese

що якщо мені потрібно одночасно оновити інші властивості стану, які не залежать від попереднього стану? Наприклад, перемикання повідомлення, що відображається, з видимим як true / false, але повідомлення змінюється залежно від стану?
hamncheez

1
@hamncheez Зворотне значення функції - це все одно ваш новий стан. Ви можете або не використовувати значення попереднього стану; так що ви можете надсилати будь-які значення, наприклад різні повідомлення.
Дейн

17

Ви повинні використовувати this.state.checkзамість check.valueтут:

this.setState({check: !this.state.check})

Але в будь-якому випадку це погана практика робити це таким чином. Набагато краще перенести його на окремий метод і не писати зворотних викликів безпосередньо в розмітці.


Піднесений, але з цікавості - чому ця "погана практика"?
Товариш незнайомий

Краще розділити подання та логіку, оскільки таким чином ви зможете бачити та оновлювати свою логіку та розмітку окремо та легше.
Євген Цах,

2
Це не просто погана практика, але ви можете не отримати бажаного результату, як setStateі асинхронізація . Дивіться мою відповідь нижче.
Дейн

1
Я думаю, що відповідь Дейна має кращий підхід, оскільки він використовує API React, призначений для використання у випадку, якщо новий стан залежить від попереднього стану.
МТ.

3
@MT. Але потім, я відповів роками пізніше. Ось це :)
Дейн

5

Використовуйте checkedдля отримання значення. Протягом onChange, checkedбуде trueі це буде тип boolean.

Сподіваюся, це допомагає!

class A extends React.Component {
  constructor() {
    super()
    this.handleCheckBox = this.handleCheckBox.bind(this)
    this.state = {
      checked: false
    }
  }
  
  handleCheckBox(e) {
    this.setState({
      checked: e.target.checked
    })
  }
  
  render(){
    return <input type="checkbox" onChange={this.handleCheckBox} checked={this.state.checked} />
  }
}

ReactDOM.render(<A/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>


4

Ось приклад використання хуків (потрібно React> = 16.8.0)

// import React, { useState } from 'react';
const { useState } = React;

function App() {
  const [checked, setChecked] = useState(false);
  const toggleChecked = () => setChecked(value => !value);
  return (
    <input
      type="checkbox"
      checked={checked}
      onChange={toggleChecked}
    />
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<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>


3

Ви також можете скористатися useStateгачком React для оголошення локального стану функціонального компонента. Початковий стан змінної toggledпередано як аргумент методу .useState.

import { render } from 'react-dom';
import React from "react";

type Props = {
  text: string,
  onClick(event: React.MouseEvent<HTMLButtonElement>): void,
};

export function HelloWorldButton(props: Props) {
  const [toggled, setToggled] = React.useState(false); // returns a stateful value, and a function to update it
  return <button
  onClick={(event) => {
    setToggled(!toggled);
    props.onClick(event);
  }}
  >{props.text} (toggled: {toggled.toString()})</button>;
}


render(<HelloWorldButton text='Hello World' onClick={() => console.log('clicked!')} />, document.getElementById('root'));

https://stackblitz.com/edit/react-ts-qga3vc


2

Спробуйте:

<label><input type=checkbox" value="check" onChange = {(e) => this.setState({check: !this.state.check.value})}/> Checkbox </label>

Використовуючи check: !check.valueзасоби, він шукає checkоб’єкт, який ви не оголосили.

Вам потрібно вказати, що ви хочете протилежне значення this.state.check.


2

Я знайшов це найпростішим при перемиканні булевих значень. Простіше кажучи, якщо значення вже має значення true, тоді воно встановлює значення false і навпаки. Остерігайтеся невизначених помилок, переконайтесь, що ваше властивість було визначено перед виконанням

this.setState({
   propertyName: this.propertyName = !this.propertyName
});

! Дякую за це. У мене це спрацювало.
Пт1,

2

Залежно від вашого контексту; це дозволить вам оновити стан, враховуючи функцію mouseEnter. У будь-якому випадку, встановивши для значення стану будь-яке значення true: false, ви можете оновити це значення стану для будь-якої функції, встановивши для нього протилежне значення за допомогою!this.state.variable

state = {
  hover: false
}

onMouseEnter = () => {
  this.setState({
    hover: !this.state.hover
  });
};

0

Я потрапив на цю сторінку, коли шукаю використання стану перемикання в компоненті React за допомогою Redux, але я не знаходжу тут жодного підходу з використанням того самого.

Отже, я думаю, це може допомогти тому, хто намагався реалізувати перемикання стану за допомогою Redux.

Мій файл редуктора знаходиться тут. Я отримую початковий стан false за замовчуванням.

const INITIAL_STATE = { popup: false };
export default (state = INITIAL_STATE, action) => {
    switch (action.type) {
        case "POPUP":
            return {
                ...state,
                popup: action.value
            };
        default:
            return state;
    }
    return state;
};

Я змінюю стан, натискаючи зображення. Отже, мій тег img йде сюди з функцією onClick.

<img onClick={togglePopup} src={props.currentUser.image} className="avatar-image avatar-image--icon" />

Моя функція Toggle Popup знаходиться нижче, що викликає Dispatcher.

const togglePopup = ev => {
    ev.preventDefault();
    props.handlePopup(!props.popup);
};

Цей виклик переходить до функції mapDispatchToProps, яка відображає назад переключений стан.

const mapDispatchToProps = dispatch => ({
    handlePopup: value => dispatch({ type: "POPUP", value })
});

Дякую.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.