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?

dKab
fuente

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>

Fabián Schultz
fuente
¡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>
    );
}
Varnika Chaturvedi
fuente
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}

T Mitchell
fuente
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>

prosti
fuente
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

maldita sea
fuente
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>
Agnibha Chatterjee
fuente
0

solo agrega:

<button disabled={this.input.value?"true":""} className="add-item__button" onClick={this.add.bind(this)}>Add</button>
nh Dalim
fuente
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

novato
fuente