Cómo deshabilitar el botón en React.js


84

Tengo este componente:

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

}

Quiero que el botón esté desactivado cuando el valor de entrada esté vacío. Pero el código anterior no funciona. Dice:

add-item.component.js: 78 Uncaught TypeError: No se puede leer la propiedad 'valor' de indefinido

señalando disabled={!this.input.value}. ¿Qué puedo estar haciendo mal aquí? Supongo que tal vez ref aún no se crea cuando renderse ejecuta el método. Si, entonces, ¿cuál es la solución alternativa?

Respuestas:


109

Usar refsno es una buena práctica porque lee el DOM directamente, es mejor usar React's stateen su lugar. Además, su botón no cambia porque el componente no se vuelve a renderizar y permanece en su estado inicial.

Puede usarlo setStatejunto con un onChangedetector de eventos para volver a representar el componente cada vez que cambia el campo de entrada:

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

Aquí hay un ejemplo de trabajo:

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>


¡Esta es la respuesta correcta! El estado interno debe usarse para actualizar al cambiar la entrada y el botón simplemente debe verificar esto. +1
Michael Giovanni Pumo

this.state tiene la limitación de que vuelve a mostrar la interfaz de usuario, por lo que si está escribiendo en un cuadro de texto en un componente secundario y se ha vinculado al evento onChange, pierde el foco del cuadro de texto. Si está en el mismo componente, está bien, pero cuando se usa en un componente secundario, pierde el foco.
Dinámico

@Dynamic Eso no debería suceder. ¿Tiene un ejemplo práctico de eso?
Fabian Schultz

Para su información, esto no es compatible con varios navegadores. Muchos navegadores aún interpretan la presencia de disabled como verdadero incluso si se establece en falso. Como si literalmente solo verificara la presencia de atributos deshabilitados. Debe eliminar el atributo deshabilitado para que sea compatible con todos los navegadores.
Adrianópolis

@Adrianopolis React elimina el disabledatributo del DOM si está configurado en false; esto es compatible con todos los navegadores.
Fabian Schultz

9

En HTML,

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

Todos ellos se reducen a disabled = "true", eso se debe a que devuelve true para una cadena no vacía. Por lo tanto, para devolver falso, pase una cadena vacía en una declaración condicional como 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>
    );
}

si el desbordamiento de pila tiene una opción de respuesta de apoyo, seleccionaría esta respuesta
Feras

6

No debería establecer el valor de la entrada a través de refs.

Eche un vistazo a la documentación de los componentes de formularios controlados aquí: https://facebook.github.io/react/docs/forms.html#controlled-components

En una palabra

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

Entonces podrá controlar el estado deshabilitado usando disabled={!this.state.value}


4

Hay algunos métodos típicos de cómo controlamos la representación de componentes en React. ingrese la descripción de la imagen aquí

Pero, no he usado ninguno de estos aquí, solo usé las referencias para el espacio de nombres subyacente a los niños del componente.

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.inputno está definido hasta que refse llame a la devolución de llamada. Intente establecer this.inputun valor inicial en su constructor.

De los documentos de React sobre referencias , el énfasis es mío:

la devolución de llamada se ejecutará inmediatamente después de que se monte o desmonte el componente


0

He tenido un problema similar, resulta que no necesitamos ganchos para hacer esto, podemos hacer un render condicional y seguirá funcionando bien.

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

0

solo agrega:

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

0

Una solución muy simple para esto es usar useRefhook

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>

Del mismo modo, puede habilitar el botón utilizando buttonRef.current.disabled = false

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.