Как отключить кнопку в React.js

86

У меня такой компонент:

import React from 'react';

export default class AddItem extends React.Component {

add() {
    this.props.onButtonClick(this.input.value);
    this.input.value = '';
}


render() {
    return (
        <div className="add-item">
            <input type="text" className="add-item__input" ref={(input) => this.input = input} placeholder={this.props.placeholder} />
            <button disabled={!this.input.value} className="add-item__button" onClick={this.add.bind(this)}>Add</button>
        </div>
    );
}

}

Я хочу, чтобы кнопка была отключена, когда входное значение пусто. Но приведенный выше код не работает. Он говорит:

add-item.component.js: 78 Uncaught TypeError: невозможно прочитать значение свойства undefined

указывая на disabled={!this.input.value}. Что я здесь делаю не так? Я предполагаю, что, возможно, ref еще не создается при выполнении renderметода. Если, так что же такое обходной путь?

dKab
источник

Ответы:

115

Использование refs- не лучшая практика, потому что он читает DOM напрямую, stateвместо этого лучше использовать React . Кроме того, ваша кнопка не меняется, потому что компонент не перерисовывается и остается в исходном состоянии.

Вы можете использовать setStateвместе со onChangeслушателем событий для повторного рендеринга компонента каждый раз, когда поле ввода изменяется:

// Input field listens to change, updates React's state and re-renders the component.
<input onChange={e => this.setState({ value: e.target.value })} value={this.state.value} />

// Button is disabled when input state is empty.
<button disabled={!this.state.value} />

Вот рабочий пример:

class AddItem extends React.Component {
  constructor() {
    super();
    this.state = { value: '' };
    this.onChange = this.onChange.bind(this);
    this.add = this.add.bind(this);
  }

  add() {
    this.props.onButtonClick(this.state.value);
    this.setState({ value: '' });
  }

  onChange(e) {
    this.setState({ value: e.target.value });
  }

  render() {
    return (
      <div className="add-item">
        <input
          type="text"
          className="add-item__input"
          value={this.state.value}
          onChange={this.onChange}
          placeholder={this.props.placeholder}
        />
        <button
          disabled={!this.state.value}
          className="add-item__button"
          onClick={this.add}
        >
          Add
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <AddItem placeholder="Value" onButtonClick={v => console.log(v)} />,
  document.getElementById('View')
);
<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='View'></div>

Фабиан Шульц
источник
Это правильный ответ! Внутреннее состояние должно использоваться для обновления при изменении ввода, и кнопка должна просто проверять это. +1
Майкл Джованни Пумо
this.state имеет ограничение, заключающееся в том, что он снова отображает пользовательский интерфейс, поэтому, если вы вводите текст в текстовое поле в дочернем компоненте и привязаны к событию onChange, он теряет фокус из текстового поля. Если он находится в том же компоненте, это нормально, но при использовании в дочернем компоненте он теряет фокус.
Dynamic
@Dynamic Этого не должно происходить. У вас есть рабочий пример этого?
Фабиан Шульц
К вашему сведению, это не кроссбраузерно. Многие браузеры по-прежнему интерпретируют наличие disabled как истинное, даже если установлено значение false. Вроде буквально просто проверяет наличие отключенного атрибута. Чтобы обеспечить кроссбраузерность, необходимо удалить атрибут disabled.
Адрианополис
@Adrianopolis React удаляет disabledатрибут из DOM, если он установлен в значение false- это кроссбраузерно.
Fabian Schultz
13

В HTML

<button disabled/>
<buttton disabled="true">
<buttton disabled="false">
<buttton disabled="21">

Все они сводятся к disabled = "true", потому что он возвращает true для непустой строки. Следовательно, чтобы вернуть false, передайте пустую строку в условном выражении, например this.input.value? "True": "" .

render() {
    return (
        <div className="add-item">
            <input type="text" className="add-item__input" ref={(input) => this.input = input} placeholder={this.props.placeholder} />
            <button disabled={this.input.value?"true":""} className="add-item__button" onClick={this.add.bind(this)}>Add</button>
        </div>
    );
}
Варника Чатурведи
источник
если у stack-overflow есть вариант поддерживающего ответа, я бы выбрал этот ответ
Feras,
6

Вы не должны устанавливать значение ввода через refs.

Взгляните на документацию по компонентам контролируемой формы здесь - https://facebook.github.io/react/docs/forms.html#controlled-components

В двух словах

<input value={this.state.value} onChange={(e) => this.setState({value: e.target.value})} />

Тогда вы сможете управлять отключенным состоянием, используя disabled={!this.state.value}

Т. Митчелл
источник
4

Существует несколько типичных методов управления рендерингом компонентов в React. введите описание изображения здесь

Но я не использовал здесь ничего из этого, я просто использовал ссылку для пространства имен, лежащих в основе дочерних элементов компонента.

class AddItem extends React.Component {
    change(e) {
      if ("" != e.target.value) {
        this.button.disabled = false;
      } else {
        this.button.disabled = true;
      }
    }

    add(e) {
      console.log(this.input.value);
      this.input.value = '';
      this.button.disabled = true;
    }

    render() {
        return (
          <div className="add-item">
          <input type="text" className = "add-item__input" ref = {(input) => this.input=input} onChange = {this.change.bind(this)} />
          
          <button className="add-item__button" 
          onClick= {this.add.bind(this)} 
          ref={(button) => this.button=button}>Add
          </button>
          </div>
        );
    }
}

ReactDOM.render(<AddItem / > , document.getElementById('root'));
<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="root"></div>

Прости
источник
2

this.inputне определено, пока не refбудет вызван обратный вызов. Попробуйте установить this.inputкакое-то начальное значение в своем конструкторе.

Из документации React по ссылкам , акцент мой:

обратный вызов будет выполнен сразу после монтирования или размонтирования компонента

чертовски
источник
1

У меня была аналогичная проблема, оказалось, что для этого нам не нужны хуки, мы можем сделать условный рендер, и он все равно будет работать нормально.

<Button
    type="submit"
    disabled={
        name === "" || email === "" || password === "" ? true : false
    }
    fullWidth
    variant="contained"
    color="primary"
    className={classes.submit}>
    SignUP
</Button>
Агнибха Чаттерджи
источник
Классное решение, сделал то же самое
Симба
1

очень простое решение - использовать useRefкрючок

const buttonRef = useRef();

const disableButton = () =>{
  buttonRef.current.disabled = true; // this disables the button
 }

<button
className="btn btn-primary mt-2"
ref={buttonRef}
onClick={disableButton}
>
    Add
</button>

Точно так же вы можете включить кнопку, используя buttonRef.current.disabled = false

новичок
источник
0

просто добавь:

<button disabled={this.input.value?"true":""} className="add-item__button" onClick={this.add.bind(this)}>Add</button>
nh Dalim
источник