Jak wyłączyć przycisk w React.js

86

Mam ten komponent:

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

}

Chcę, aby przycisk był wyłączony, gdy wartość wejściowa jest pusta. Ale powyższy kod nie działa. To mówi:

add-item.component.js: 78 Uncaught TypeError: Cannot read property „value” of undefined

wskazuje na disabled={!this.input.value}. Co tu robię źle? Zgaduję, że być może ref nie jest jeszcze tworzony, gdy rendermetoda jest wykonywana. Jeśli tak, jakie jest obejście?

dKab
źródło

Odpowiedzi:

116

Używanie refsnie jest najlepszą praktyką, ponieważ czyta bezpośrednio DOM, lepiej statezamiast tego użyć Reacta . Ponadto przycisk nie zmienia się, ponieważ komponent nie jest ponownie renderowany i pozostaje w swoim stanie początkowym.

Możesz używać setStaterazem z onChangedetektorem zdarzeń, aby renderować komponent ponownie za każdym razem, gdy zmieni się pole wejściowe:

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

Oto działający przykład:

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>

Fabian Schultz
źródło
To jest poprawna odpowiedź! Stan wewnętrzny powinien być użyty do aktualizacji przy zmianie wejścia i przycisk powinien po prostu to sprawdzić. +1
Michael Giovanni Pumo
this.state ma ograniczenie polegające na tym, że ponownie renderuje interfejs użytkownika, więc jeśli piszesz w polu tekstowym w składniku podrzędnym i jesteś powiązany ze zdarzeniem onChange, traci fokus z pola tekstowego. Jeśli jest w tym samym komponencie, jest w porządku, ale gdy jest używany w komponencie podrzędnym, traci fokus.
Dynamiczny
@Dynamic To nie powinno się zdarzyć. Czy masz tego działający przykład?
Fabian Schultz
Do Twojej wiadomości, to nie jest przyjazne dla różnych przeglądarek. Wiele przeglądarek nadal interpretuje obecność opcji disabled jako true, nawet jeśli jest ustawiona na false. Dosłownie po prostu sprawdza obecność wyłączonego atrybutu. Musisz usunąć wyłączony atrybut, aby był przyjazny dla różnych przeglądarek.
Adrianopolis
@Adrianopolis React usuwa disabledatrybut z DOM, jeśli jest ustawiony na false- jest to przyjazne dla różnych przeglądarek.
Fabian Schultz
13

W HTML

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

Wszystkie sprowadzają się do disabled = "true", ponieważ zwraca prawdę dla niepustego ciągu. Dlatego w celu zwrócenia fałszu należy przekazać pusty ciąg w instrukcji warunkowej, takiej jak 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>
    );
}
Varnika Chaturvedi
źródło
jeśli przepełnienie stosu ma opcję odpowiedzi wspierającej,
wybrałbym
6

Nie powinieneś ustawiać wartości wejścia za pomocą ref.

Zapoznaj się z dokumentacją dotyczącą kontrolowanych składników formularzy tutaj - https://facebook.github.io/react/docs/forms.html#controlled-components

W skrócie

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

Wtedy będziesz mógł kontrolować stan wyłączenia za pomocą disabled={!this.state.value}

T Mitchell
źródło
4

Istnieje kilka typowych metod kontrolowania renderowania komponentów w Reakcie. wprowadź opis obrazu tutaj

Ale nie użyłem tutaj żadnego z nich, po prostu użyłem ref do przestrzeni nazw podstawowych elementów potomnych składnika.

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>

prosti
źródło
2

this.inputjest niezdefiniowana, dopóki nie refzostanie wywołana funkcja zwrotna. Spróbuj ustawić this.inputjakąś wartość początkową w konstruktorze.

Z dokumentacji React na temat referencji , nacisk mój:

wywołanie zwrotne zostanie wykonane natychmiast po zamontowaniu lub odmontowaniu komponentu

cholera
źródło
1

Miałem podobny problem, okazuje się, że nie potrzebujemy do tego hooków, możemy wykonać renderowanie warunkowe i nadal będzie działać dobrze.

<Button
    type="submit"
    disabled={
        name === "" || email === "" || password === "" ? true : false
    }
    fullWidth
    variant="contained"
    color="primary"
    className={classes.submit}>
    SignUP
</Button>
Agnibha Chatterjee
źródło
Fajne rozwiązanie, zrobiłem to samo
Simba
1

bardzo prostym rozwiązaniem jest użycie useRefhaka

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>

Podobnie możesz włączyć przycisk za pomocą buttonRef.current.disabled = false

Nowicjusz
źródło
0

poprostu dodaj:

<button disabled={this.input.value?"true":""} className="add-item__button" onClick={this.add.bind(this)}>Add</button>
nh Dalim
źródło