So deaktivieren Sie die Schaltfläche in React.js

84

Ich habe diese Komponente:

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

}

Ich möchte, dass die Schaltfläche deaktiviert wird, wenn der Eingabewert leer ist. Der obige Code funktioniert jedoch nicht. Es sagt:

add-item.component.js: 78 Nicht erfasster TypeError: Die Eigenschaft 'value' von undefined kann nicht gelesen werden

zeigen auf disabled={!this.input.value}. Was kann ich hier falsch machen? Ich vermute, dass ref möglicherweise noch nicht erstellt wurde, wenn die renderMethode ausgeführt wird. Wenn ja, wie lautet die Problemumgehung?

dKab
quelle

Antworten:

109

Die Verwendung refsist keine bewährte Methode, da das DOM direkt gelesen wird. Es ist besser, statestattdessen die von React zu verwenden. Außerdem ändert sich Ihre Schaltfläche nicht, da die Komponente nicht neu gerendert wird und im Ausgangszustand bleibt.

Sie können setStatezusammen mit einem onChangeEreignis-Listener die Komponente jedes Mal neu rendern, wenn sich das Eingabefeld ändert:

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

Hier ist ein Arbeitsbeispiel:

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
quelle
Das ist die richtige Antwort! Der interne Status sollte verwendet werden, um bei Änderung der Eingabe zu aktualisieren, und die Schaltfläche sollte dies einfach überprüfen. +1
Michael Giovanni Pumo
this.state hat die Einschränkung, dass die Benutzeroberfläche erneut gerendert wird. Wenn Sie also ein Textfeld in eine untergeordnete Komponente eingeben und an das Ereignis onChange gebunden haben, verliert es den Fokus aus dem Textfeld. Wenn es sich in derselben Komponente befindet, ist es in Ordnung, aber wenn es in einer untergeordneten Komponente verwendet wird, verliert es den Fokus.
Dynamisch
@ Dynamic Das sollte nicht passieren. Haben Sie ein funktionierendes Beispiel dafür?
Fabian Schultz
Nur zu Ihrer Information, dies ist nicht browserübergreifend. Viele Browser interpretieren das Vorhandensein von deaktiviert immer noch als wahr, selbst wenn es auf falsch gesetzt ist. Wie es buchstäblich nur prüft, ob das Attribut deaktiviert ist. Sie müssen das deaktivierte Attribut entfernen, um browserübergreifend zu sein.
Adrianopolis
@Adrianopolis React entfernt das disabledAttribut aus dem DOM, wenn es auf gesetzt ist false- dies ist browserübergreifend.
Fabian Schultz
9

In HTML

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

Alle von ihnen laufen auf disabled = "true" hinaus, da für eine nicht leere Zeichenfolge true zurückgegeben wird. Um false zurückzugeben, übergeben Sie daher eine leere Zeichenfolge in einer bedingten Anweisung wie 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
quelle
Wenn der Stapelüberlauf eine unterstützende Antwortoption bietet, würde ich diese Antwort auswählen
Feras
6

Sie sollten den Wert der Eingabe nicht über refs einstellen.

Sehen Sie sich hier die Dokumentation zu kontrollierten Formularkomponenten an - https://facebook.github.io/react/docs/forms.html#controlled-components

In einer Nussschale

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

Dann können Sie den deaktivierten Status mithilfe von steuern disabled={!this.state.value}

T Mitchell
quelle
4

Es gibt nur wenige typische Methoden, wie wir das Rendern von Komponenten in React steuern. Geben Sie hier die Bildbeschreibung ein

Aber ich habe hier keine davon verwendet, sondern nur die Refs verwendet, um der Komponente zugrunde liegende untergeordnete Elemente zu benennen.

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
quelle
2

this.inputist undefiniert, bis der refRückruf aufgerufen wird. Versuchen Sie, this.inputin Ihrem Konstruktor einen Anfangswert festzulegen.

Aus den React-Dokumenten zu Refs , Hervorhebung von mir:

Der Rückruf wird sofort ausgeführt, nachdem die Komponente ein- oder ausgehängt wurde

verdammt
quelle
0

Ich hatte ein ähnliches Problem. Es stellte sich heraus, dass wir dafür keine Hooks benötigen. Wir können ein bedingtes Rendern erstellen und es wird immer noch gut funktionieren.

<Button
    type="submit"
    disabled={
        name === "" || email === "" || password === "" ? true : false
    }
    fullWidth
    variant="contained"
    color="primary"
    className={classes.submit}>
    SignUP
</Button>
Agnibha Chatterjee
quelle
0

einfach hinzufügen:

<button disabled={this.input.value?"true":""} className="add-item__button" onClick={this.add.bind(this)}>Add</button>
nh Dalim
quelle
0

Eine sehr einfache Lösung hierfür ist die Verwendung eines useRefHakens

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>

Ebenso können Sie die Schaltfläche mit aktivieren buttonRef.current.disabled = false

Neuling
quelle