¿Usar estado o referencias en componentes de formulario React.js?

116

Estoy comenzando con React.js y quiero hacer un formulario simple pero en la documentación he encontrado dos formas de hacerlo.

El primero está usando Refs :

var CommentForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();
    var author = React.findDOMNode(this.refs.author).value.trim();
    var text = React.findDOMNode(this.refs.text).value.trim();
    if (!text || !author) {
      return;
    }
    // TODO: send request to the server
    React.findDOMNode(this.refs.author).value = '';
    React.findDOMNode(this.refs.text).value = '';
    return;
  },
  render: function() {
    return (
      <form className="commentForm" onSubmit={this.handleSubmit}>
        <input type="text" placeholder="Your name" ref="author" />
        <input type="text" placeholder="Say something..." ref="text" />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

Y el segundo está usando el estado dentro del componente React:

var TodoTextInput = React.createClass({
  getInitialState: function() {
    return {
      value: this.props.value || ''
    };
  },

  render: function() /*object*/ {
    return (
      <input className={this.props.className}
      id={this.props.id}
      placeholder={this.props.placeholder}
      onBlur={this._save}
      value={this.state.value}
      />
    );
  },

  _save: function() {
    this.props.onSave(this.state.value);
    this.setState({value: ''
  });
});

No veo los pros y los contras de las dos alternativas, si es que existe alguna. Gracias.

Gabrielgiussi
fuente
¿Me estoy perdiendo de algo? ¿Por qué no usa el objeto de evento para obtener los valores del formulario? Esa parece ser la única razón para usar un formulario aquí en primer lugar. Si no está utilizando el comportamiento de envío predeterminado y tiene referencias en las entradas, no necesita envolverlas en un formulario.
NectarSoft

Respuestas:

143

La versión corta: evita las referencias.


Son malos para la mantenibilidad y pierden gran parte de la simplicidad que proporciona el renderizado del modelo WYSIWYG.

Tienes una forma. Necesita agregar un botón que restablezca el formulario.

  • refs:
    • manipular el DOM
    • render describe cómo se veía el formulario hace 3 minutos
  • estado
    • setState
    • render describe cómo se ve el formulario

Tiene un campo de número CCV en una entrada y algunos otros campos en su aplicación que son números. Ahora necesita hacer cumplir que el usuario solo ingrese números.

  • refs:
    • agregue un controlador onChange (¿no estamos usando referencias para evitar esto?)
    • manipular dom en onChange si no es un número
  • estado
    • ya tienes un controlador onChange
    • agregue una declaración if, si no es válida, no haga nada
    • render solo se llama si va a producir un resultado diferente

Eh, no importa, el primer ministro quiere que hagamos una sombra de caja roja si no es válido.

  • refs:
    • hacer que el controlador onChange simplemente llame a forceUpdate o algo así?
    • hacer la salida de render basada en ... ¿eh?
    • ¿De dónde obtenemos el valor para validar en render?
    • manipular manualmente la propiedad dom className de un elemento?
    • estoy perdido
    • reescribir sin referencias?
    • leer del dom en render si estamos montados, de lo contrario, ¿asumimos que es válido?
  • estado:
    • eliminar la declaración if
    • hacer render validar basado en this.state

Necesitamos devolver el control a los padres. Los datos ahora están en accesorios y debemos reaccionar a los cambios.

  • refs:
    • implementar componentDidMount, componentWillUpdate y componentDidUpdate
    • diferenciar manualmente los accesorios anteriores
    • manipular el dom con el mínimo conjunto de cambios
    • ¡Oye! estamos implementando reaccionar en reaccionar ...
    • hay más, pero me duelen los dedos
  • estado:
    • sed -e 's/this.state/this.props/' 's/handleChange/onChange/' -i form.js

La gente piensa que los árbitros son "más fáciles" que mantenerlo en regla. Esto puede ser cierto durante los primeros 20 minutos, no es cierto en mi experiencia después de eso. Póngase en posición de decir "Sí, lo terminaré en 5 minutos" en lugar de "Claro, solo reescribiré algunos componentes".

Bandido
fuente
3
¿Podría explicar un poco más sobre sed -e 's / this.state / this.props /' 's / handleChange / onChange /' -i form.js?
gabrielgiussi
1
No, me refiero a cambios reales en el dom. React.findDOMNode(this.refs.foo). Si, por ejemplo, cambia, this.refs.foo.props.barno pasará nada.
Brigand
1
por ejemplo, si lo ha <input onChange={this.handleChange} value={this.state.foo} />cambiado a <input onChange={this.props.handleChange} value={this.props.foo} />, o ha modificado su función handleChange para llamar a la devolución de llamada en props. De cualquier manera, son algunos pequeños cambios obvios.
Brigand
4
No estoy seguro de si soy el único que encuentra su respuesta un poco confusa. ¿Podría mostrar algunos ejemplos de código para aclarar sus puntos?
Rishabh
2
Tener más de 50 entradas en una pantalla y renderizar cada una en cualquier cambio de estado no es deseable. Componentizar cada inputcampo donde cada uno mantiene su propio estado es ideal. En algún momento necesitamos reconciliar estos diversos estados independientes con algún modelo más grande. Tal vez tengamos un autoguardado en un temporizador, o simplemente lo guardemos en. componentWillUnmountAquí es donde encuentro refsideal, durante la reconciliación sacamos el statevalor de cada uno ref, y ninguno es más sabio. Estoy de acuerdo en que en la mayoría de los casos statees la respuesta, pero con una gran cantidad de inputs, utilizar un refspatrón adecuado es una bendición para el rendimiento
lux
105

He visto a algunas personas citar la respuesta anterior como una razón para "nunca usar referencias" y quiero dar mi opinión (así como a algunos otros desarrolladores de React con los que he hablado).

El sentimiento de "no usar referencias" es correcto cuando se habla de usarlos para instancias de componentes. Es decir, no debe usar referencias como una forma de tomar instancias de componentes y llamar a métodos en ellas. Esta es la forma incorrecta de usar las referencias y es cuando las referencias van al sur rápidamente.

La forma correcta (y muy útil) de usar las referencias es cuando las usa para obtener algún valor del DOM. Por ejemplo, si tiene un campo de entrada que adjunta una referencia a esa entrada, entonces tomar el valor más tarde a través de la referencia está bien. Sin esta forma, debe pasar por un proceso bastante orquestado para mantener su campo de entrada actualizado con su estado local o su tienda de flujo, lo que parece innecesario.

Edición de 2019: Hola amigos del futuro. Además de lo que mencioné hace unos años ^, con React Hooks, las referencias también son una excelente manera de realizar un seguimiento de los datos entre renderizaciones y no se limitan a solo tomar nodos DOM.

Tyler McGinnis
fuente
3
Tu último párrafo tiene mucho sentido, pero ¿puedes aclarar tu segundo párrafo? ¿Cuál es un ejemplo concreto de tomar una instancia de componente y llamar a un método que se consideraría incorrecto?
Danny Libin
2
Estoy de acuerdo con esto. Utilizo referencias a menos que / hasta que necesite hacer una validación o manipulación del valor de un campo. Si necesito validar el cambio o cambiar los valores mediante programación, entonces uso state.
Christopher Davies
1
Estoy de acuerdo con esto tambien. Durante una fase de descubrimiento, me acerqué intencionalmente a una pantalla con una gran cantidad de entradas con ingenuidad. Todos los valores de entrada almacenados en un mapa (en estado) codificados por id. No hace falta decir que el rendimiento se vio afectado desde que se configuró el estado y se procesaron más de 50 entradas (¡algunos materiales-ui, que eran pesados!) En cambios menores en la interfaz de usuario, ya que hacer clic en una casilla de verificación no era ideal. Componentizar cada entrada que puede mantener su propio estado parecía el enfoque adecuado. Si se necesita una reconciliación, mire en el refsy obtenga el valor del estado. En realidad, parece un patrón muy bonito.
lux
2
Estoy completamente de acuerdo. La respuesta aceptada es demasiado vaga en mi opinión.
James Wright
Estoy de acuerdo. Al diseñar un componente de formulario general, esto saca a la luz los puntos débiles de los componentes controlados y la gestión del enfoque, el manejo de errores, etc. No es posible tener una arquitectura limpia de hecho. Habla conmigo si es necesario. Estoy moviendo mis componentes a refs.
kushalvm
6

TL; DR En términos generales, refsvaya en contra de la filosofía declarativa de React , por lo que debe usarlos como último recurso. Úselo state / propssiempre que sea posible.


Para entender dónde usa refsvs state / props, veamos algunos de los principios de diseño que sigue React.

Según la documentación de React sobrerefs

Evite el uso de referencias para cualquier cosa que pueda hacerse de forma declarativa.

Principios de diseño de Per React sobre las escotillas de escape

Si algún patrón que sea útil para crear aplicaciones es difícil de expresar de manera declarativa, proporcionaremos una API imperativa para ello. (y enlazan a las referencias aquí)

Lo que significa que el equipo de React sugiere evitar refsy usar state / propspara cualquier cosa que se pueda hacer de manera reactiva / declarativa.

@Tyler McGinnis ha proporcionado una muy buena respuesta , indicando también que

La forma correcta (y muy útil) de usar las referencias es cuando las usa para obtener algún valor del DOM ...

Si bien puede hacer eso, estará trabajando en contra de la filosofía de React. Si tiene valor en una entrada, seguramente proviene de state / props. Para mantener el código coherente y predecible, también debes ceñirte a state / propsél. Reconozco el hecho de que a refsveces te da la solución más rápida, por lo que si haces una prueba de concepto, rápido y sucio es aceptable.

Esto nos deja con varios casos de uso concretos pararefs

Administrar el enfoque, la selección de texto o la reproducción de medios. Activación de animaciones imperativas. Integración con bibliotecas DOM de terceros.

Lyubomir
fuente
5

Esta publicación es antigua.

Compartiré mi pequeña experiencia en un caso sobre ese tema.

Estaba trabajando en un componente grande (414 líneas) con muchas entradas 'dinámicas' y muchos datos en caché involucrados. (No estoy trabajando solo en la página, y mis sentidos me dicen que la estructura del código probablemente podría dividirse mejor, pero no es el punto (bueno, podría serlo, pero estoy lidiando con eso)

Primero trabajé con el estado para manejar los valores de las entradas:

  const [inputsValues, setInputsValues] = useState([])
  const setInputValue = (id, value) => {
    const arr = [...inputsValues]
    arr[id] = value
    setInputsValues(arr)
  }

y por supuesto en las entradas:

value={inputsValues[id] || ''}
onChange={event => setInputValue(id, event.target.value)}

El renderizado era tan pesado que el cambio de entrada estaba entrecortado como **** (no intente mantener la tecla presionada, el texto solo aparecerá después de una pausa)

Estaba seguro de que podría evitar esto usando refs.

terminó así:

  const inputsRef = useRef([])

y en las entradas:

ref={input => (inputsRef.current[id] = input)}

[Bueno, en mi caso, la entrada fue Material-UI TextField, por lo que fue:

inputRef={input => (inputsRef.current[id] = input)}

]

Gracias a esto, no hay re-renderización, la entrada es fluida, la funcionalidad funciona de la misma manera. Ahorrará ciclos y cálculo, y también energía. Hazlo por la tierra x)

Mi conclusión: useRef para el valor de las entradas incluso puede ser necesario.

Kaphar
fuente