Componentes funcionales con ganchos de reacción. ¿Por qué es mejor?

Cuando se lanzó React.js 16.8, tuvimos la oportunidad de usar React Hooks. Los ganchos nos permiten escribir componentes completamente funcionales usando funciones. Podemos usar todas las funciones de React.js y hacerlo de manera más conveniente.


Mucha gente no está de acuerdo con la concepción de Hooks. En este artículo me gustaría contarle algunas ventajas importantes que React Hooks le brinda y por qué necesitamos escribir con Hooks.


No hablaré sobre cómo usar ganchos. No es muy importante para los ejemplos. Si desea leer algo sobre este tema, puede utilizar la documentación oficial . Además, si este tema le resulta interesante, escribiré más sobre Hooks.


Los ganchos nos permiten reutilizar nuestro código fácilmente


Imaginemos un componente que representa una forma simple. Puede ser algo que nos muestre algunas entradas y nos permita cambiar sus valores.


Con la notación de clase, habrá algo como esto:


class Form extends React.Component { state = { // Fields values fields: {}, }; render() { return ( <form> {/* Inputs render */} </form> ); }; } 

Imaginemos ahora que queremos guardar automáticamente nuestros valores de campo en un backend cada vez que cambian. Sugiero omitir la definición de funciones externas como shallowEqual y debounce .


 class Form extends React.Component { constructor(props) { super(props); this.saveToDraft = debounce(500, this.saveToDraft); }; state = { // Fields values fields: {}, // Draft saving meta draft: { isSaving: false, lastSaved: null, }, }; saveToDraft = (data) => { if (this.state.isSaving) { return; } this.setState({ isSaving: true, }); makeSomeAPICall().then(() => { this.setState({ isSaving: false, lastSaved: new Date(), }) }); } componentDidUpdate(prevProps, prevState) { if (!shallowEqual(prevState.fields, this.state.fields)) { this.saveToDraft(this.state.fields); } } render() { return ( <form> {/* Draft saving meta render */} {/* Inputs render */} </form> ); }; } 

El mismo componente con ganchos:


 const Form = () => { // Our state const [fields, setFields] = useState({}); const [draftIsSaving, setDraftIsSaving] = useState(false); const [draftLastSaved, setDraftLastSaved] = useState(false); useEffect(() => { const id = setTimeout(() => { if (draftIsSaving) { return; } setDraftIsSaving(true); makeSomeAPICall().then(() => { setDraftIsSaving(false); setDraftLastSaved(new Date()); }); }, 500); return () => clearTimeout(id); }, [fields]); return ( <form> {/* Draft saving meta render */} {/* Inputs render */} </form> ); } 

Como vemos, no hay una gran diferencia aquí. Reemplazamos this.state con useState hook y guardando el borrador en useEffect hook ahora.


La diferencia que quiero mostrar aquí es (también hay otras diferencias, pero me concentraré en esta): podemos extraer fácilmente este código de nuestro componente y usarlo en otro lugar:


 // useDraft hook can be used in any other component const useDraft = (fields) => { const [draftIsSaving, setDraftIsSaving] = useState(false); const [draftLastSaved, setDraftLastSaved] = useState(false); useEffect(() => { const id = setTimeout(() => { if (draftIsSaving) { return; } setDraftIsSaving(true); makeSomeAPICall().then(() => { setDraftIsSaving(false); setDraftLastSaved(new Date()); }); }, 500); return () => clearTimeout(id); }, [fields]); return [draftIsSaving, draftLastSaved]; } const Form = () => { // Our state const [fields, setFields] = useState({}); const [draftIsSaving, draftLastSaved] = useDraft(fields); return ( <form> {/* Draft saving meta render */} {/* Inputs render */} </form> ); } 

¡Y podemos usar el gancho useDraft en otros componentes! Es, por supuesto, un ejemplo muy simple, pero la reutilización de código es bastante importante y el ejemplo muestra lo fácil que es con Hooks.


Los ganchos nos permiten escribir componentes de manera más intuitiva


Imaginemos un componente de clase que representa, por ejemplo, una pantalla de chat, una lista de chats y un formulario de mensaje. Así:


 class ChatApp extends React.Component { state = { currentChat: null, }; handleSubmit = (messageData) => { makeSomeAPICall(SEND_URL, messageData) .then(() => { alert(`Message is sent to chat ${this.state.currentChat}`); }); }; render() { return ( <Fragment> <ChatsList changeChat={currentChat => { this.setState({ currentChat }); }} /> <CurrentChat id={currentChat} /> <MessageForm onSubmit={this.handleSubmit} /> </Fragment> ); }; } 

Entonces imagine a nuestro usuario usando este componente de chat:


  • Abren chat 1
  • Envían un mensaje (imaginemos una red lenta)
  • Abren chat 2
  • Ven una alerta sobre su mensaje:
    • "El mensaje se envía al chat 2"

Pero se les envió un mensaje al segundo chat, ¿cómo sucedió? Fue porque el método de clase funciona con el valor actual, no con el valor que teníamos cuando comenzábamos una solicitud de mensaje. No es un gran problema con componentes simples como este, pero puede ser una fuente de errores en sistemas más complejos.


Por otro lado, los componentes funcionales actúan de otra manera:


 const ChatApp = () => { const [currentChat, setCurrentChat] = useState(null); const handleSubmit = useCallback( (messageData) => { makeSomeAPICall(SEND_URL, messageData) .then(() => { alert(`Message is sent to chat ${currentChat}`); }); }, [currentChat] ); render() { return ( <Fragment> <ChatsList changeChat={setCurrentChat} /> <CurrentChat id={currentChat} /> <MessageForm onSubmit={handleSubmit} /> </Fragment> ); }; } 

Imaginemos a nuestro usuario:


  • Abren chat 1
  • Envían un mensaje (imaginemos una red lenta)
  • Abren chat 2
  • Ven una alerta sobre su mensaje:
    • "El mensaje se envía al chat 1"

Bueno, que ha cambiado? Ahora estamos trabajando con un valor, capturado en el momento de renderizado. Estamos creando un nuevo handleSubmit cada vez que currentChat cambia. Nos permite olvidarnos de futuros cambios y pensar ahora .


Cada componente renderiza capturando todo lo que usa .


Los ganchos hacen que el ciclo de vida de los componentes desaparezca


Esta razón se cruza fuertemente con la anterior. React es una biblioteca declarativa de IU. La declaratividad hace que la creación de UI y el proceso sean mucho más fáciles. Nos permite olvidar los cambios imperativos del DOM.


Aun así, cuando usamos clases, nos enfrentamos al ciclo de vida de los componentes. Se ve así:


  • Montaje
  • Actualización (siempre que el state o los props cambien)
  • Desmontaje

Parece convincente, pero estoy convencido de que solo se debe a nuestros hábitos. No es como reaccionar.


En lugar de esto, los componentes funcionales nos permiten escribir el código de los componentes y olvidarnos del ciclo de vida. Pensamos solo en la sincronización . Escribimos que la función crea nuestra interfaz de usuario a partir de accesorios de entrada y estado interno.


Al principio, useEffect hook parece un reemplazo para componentDidMount , componentDidUpdate y otros métodos de ciclo de vida. Pero no es así. Cuando usamos useEffect le dijimos a React: "Hey, haz esto después de renderizar mi componente".


Aquí hay un buen ejemplo del gran artículo sobre useEffect :


  • Reaccionar: dame la IU cuando el estado es 0 .
  • Su componente:
    • Aquí está el resultado del renderizado: <p>You clicked 0 times</p> .
    • También recuerde ejecutar este efecto después de que haya terminado: () => { document.title = 'You clicked 0 times' } .
  • Reaccionar: claro. Actualización de la interfaz de usuario. Hola navegador, estoy agregando algunas cosas al DOM.
  • Navegador: Genial, lo pinté en la pantalla.
  • Reaccionar: OK, ahora voy a ejecutar el efecto que me diste.
    • Running () => { document.title = 'You clicked 0 times' } .

Es mucho más declarativo, ¿no?


En el cierre


React Hooks nos permite deshacernos de algunos problemas y facilitar el desarrollo. Solo necesitamos cambiar nuestro modelo mental. De hecho, el componente funcional es una función de la interfaz de usuario de los accesorios. Describen cómo debe ser todo en cualquier momento y nos ayuda a olvidarnos de los cambios.


Bueno, necesitamos aprender cómo usarlo, pero bueno, ¿escribiste los componentes de una clase correctamente la primera vez?

Source: https://habr.com/ru/post/443500/


All Articles