Reaccionar ganchos: ¿ganar o perder?

imagen


Con el lanzamiento del nuevo React 16.6.0, HOOKS (PROPUESTA) apareció en la documentación. Ahora están disponibles en react 17.0.0-alpha y se discuten en el RFC abierto : React Hooks . Veamos qué es y por qué se necesita debajo del corte.


Sí, es RFC y puede influir en la implementación final discutiendo con los creadores de reaccionar por qué eligieron este o aquel enfoque.


Echemos un vistazo a cómo se ve un gancho estándar:


import { useState } from 'react'; function Example() { // Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } 

Intenta pensar en este código, es un avance y al final del artículo ya comprenderás lo que significa. Lo primero que debe saber es que esto no rompe la compatibilidad con versiones anteriores y tal vez se agregarán en 16.7 después de recopilar comentarios y sugerencias en el RFC.


Como aseguran los muchachos, este no es un plan para eliminar las clases de un reactivo.


Además, los ganchos no reemplazan los conceptos actuales de la reacción, todo está en lugar de props / state / context / refs. Esta es solo otra forma de usar su poder.


Motivación


Los ganchos resuelven a primera vista problemas no conectados que aparecieron con el soporte de decenas de miles de componentes durante 5 años desde Facebook.


Lo más difícil es reutilizar la lógica en componentes con estado, la reacción no tiene forma de vincular el comportamiento reutilizable al componente (por ejemplo, conectarlo al repositorio). Si trabajó con React, conoce el concepto de HOC (componente de alto orden) o accesorios de renderizado. Estos son patrones suficientemente buenos, pero a veces se usan en exceso, requieren la reestructuración de los componentes para que puedan usarse, lo que generalmente hace que el código sea más engorroso. Vale la pena mirar una aplicación de reacción típica y quedará claro lo que está en juego.


imagen


Esto se llama infierno envuelto - infierno envoltorio.


Una aplicación solo de HOC es normal en las realidades actuales, conectaron el componente a la tienda / tema / localización / corvejón personalizado, creo que todos lo saben.


Queda claro que la reacción necesita otro mecanismo primitivo para separar la lógica.


Usando ganchos, podemos extraer el estado de un componente para que pueda ser probado y reutilizado. Los ganchos le permiten reutilizar la lógica de estado sin cambiar la jerarquía de componentes. Esto facilita el intercambio de enlaces entre muchos componentes o todo el sistema. Además, los componentes de la clase parecen bastante aterradores, describimos los métodos del ciclo de vida de componentDidMount / shouldComponentUpdate / componentDidUpdate , el estado del componente, crea métodos para trabajar con el estado / lado, enlaza métodos para la instancia del componente, y así puede seguir y seguir. Por lo general, dichos componentes van más allá de las líneas x, donde x es lo suficientemente difícil de entender.


Los ganchos le permiten hacer lo mismo al dividir la lógica entre los componentes en pequeñas funciones y usarlos dentro de los componentes.


Las clases son difíciles para las personas y para los automóviles.


Al observar las clases de Facebook son un gran obstáculo al aprender React. Debe comprender cómo funciona this , y no funciona como en otros lenguajes de programación, también debe recordar acerca de los controladores de eventos vinculantes. Sin oraciones de sintaxis estable, el código se ve muy detallado. Las personas entienden los patrones de estado / accesorios y el llamado flujo de datos de arriba hacia abajo, pero las clases son bastante difíciles de entender.


Especialmente si no se limita a las plantillas, no hace mucho tiempo, los muchachos de la reacción experimentaron con el diseño de componentes con Prepack y vieron resultados prometedores, pero, sin embargo, los componentes de la clase le permiten crear malos patrones no deseados que hacen que estas optimizaciones desaparezcan, las clases tampoco migran muy bien cuando Las clases de recarga en caliente hacen que no sea confiable. En primer lugar, los chicos querían proporcionar una API que admita todas las optimizaciones y funcione bien con un reinicio en caliente.


Echa un vistazo a los ganchos


Gancho de estado


El siguiente código representa un párrafo y un botón, y si hacemos clic en el botón, el valor en el párrafo se incrementará.


 import { useState } from 'react'; function Example() { // Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } 

De esto podemos concluir que este gancho funciona de manera similar con un concepto como state .
Un método useState un poco más detallado toma un argumento, este es el valor predeterminado y devuelve una tupla en la que existe el valor en sí y el método para cambiarlo, a diferencia de setState, setCount no fusionará valores, sino que simplemente lo actualizará. También podemos usar múltiples declaraciones de estado, por ejemplo:


 function ExampleWithManyStates() { // Declare multiple state variables! const [age, setAge] = useState(42); const [fruit, setFruit] = useState('banana'); const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); // ... } 

Por lo tanto, creamos varios estados a la vez y no necesitamos pensar en cómo descomponerlos de alguna manera. Por lo tanto, se puede distinguir que los ganchos son funciones que le permiten "conectarse" a los chips de los componentes de la clase, así como los ganchos no funcionan dentro de las clases, es importante recordar esto.


Gancho de efecto


A menudo, en los componentes de clase, hacemos funciones de efectos secundarios, por ejemplo, suscribirse a eventos o hacer solicitudes de datos, generalmente para esto usamos los métodos componentDidMount / componentDidUpdate


 import { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // Similar to componentDidMount and componentDidUpdate: useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } 

Cuando llamamos a useEffect le decimos a la reacción que haga un 'efecto secundario' después de actualizar los cambios en el árbol DOM. Los efectos se declaran dentro del componente, por lo tanto, tienen acceso a accesorios / estado. Además, podemos crearlos exactamente de la misma manera tanto como desee.


 function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); const [isOnline, setIsOnline] = useState(null); useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); function handleStatusChange(status) { setIsOnline(status.isOnline); } // ... 

Inmediatamente vale la pena prestar atención al segundo efecto secundario en él, devolvemos la función, hacemos esto para realizar algunas acciones después de que el componente se desmonta, en la nueva API esto se llama efectos con limpieza. Otros efectos pueden devolver cualquier cosa.


Reglas de gancho


Los ganchos son solo funciones de JavaScript, pero requieren solo dos reglas:


  • Los ganchos deben realizarse en la parte superior de la jerarquía de funciones (esto significa que no debe llamar a los ganchos en condiciones y bucles, de lo contrario, la reacción no puede garantizar el orden de ejecución de los ganchos)
  • Llame a los enganches solo en React funciones o componentes funcionales o enganche los enganches desde enganches personalizados (esto se encuentra a continuación).
    Para seguir estas reglas, los muchachos del equipo de reacción crearon un complemento de linter que arrojará un error si llamas ganchos en los componentes de la clase o en bucles y condiciones.

Ganchos personalizados


Al mismo tiempo, queremos reutilizar la lógica de los componentes con estado, por lo general se utilizan patrones HOC o de accesorios de renderizado, pero crean un volumen adicional de nuestra aplicación.
Por ejemplo, describimos la siguiente función:


 import { useState, useEffect } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); function handleStatusChange(status) { setIsOnline(status.isOnline); } useEffect(() => { ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); }; }); return isOnline; } 

Tenga en cuenta este código, será un enlace personalizado al que podremos llamar en varios componentes. Por ejemplo, así:


 function FriendStatus(props) { const isOnline = useFriendStatus(props.friend.id); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; } 

mas o menos


 function FriendListItem(props) { const isOnline = useFriendStatus(props.friend.id); return ( <li style={{ color: isOnline ? 'green' : 'black' }}> {props.friend.name} </li> ); } 

En cualquier caso, reutilizamos el estado del componente, cada llamada a la función useFriendStatus crea un estado aislado. También vale la pena señalar que el comienzo de esta función comienza con el uso de la palabra, lo que significa que es un gancho. Le recomendamos que siga este formato. Puede escribir ganchos personalizados para cualquier cosa, animaciones / suscripciones / temporizadores y mucho más.


Hay un par de ganchos más.


useContext


useContext permite usar el valor de retorno habitual en lugar de renderProps, el contexto que queremos recuperar y nos lo devolverá, para que podamos deshacernos de todos los HOC que pasaron el contexto a los accesorios.


 function Example() { const locale = useContext(LocaleContext); const theme = useContext(ThemeContext); // ... } 

Y ahora podemos usar el objeto de contexto en el valor de retorno.


useCallback


 const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], ); 

¿Con qué frecuencia tuvo que crear un componente de una clase solo para guardar una referencia a un método? Esto ya no necesita hacerse, podemos usar useCallback y nuestros componentes no se volverán a dibujar porque ha llegado un nuevo enlace a onClick.


useMemo


Devolvemos el valor de memoria, el valor de memoria significa que se calcula solo cuando uno de los argumentos ha cambiado, la segunda vez no se calculará lo mismo.


 const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); 

Sí, aquí debe duplicar los valores en la matriz para que el gancho comprenda que no han cambiado.


useRef


useRef devuelve un valor mutado, donde el campo .current se inicializará con el primer argumento, el objeto existirá mientras exista el componente.
El ejemplo más común cuando se enfoca en la entrada


 function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { // `current` points to the mounted text input element inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> ); } 

useImperativeMethods


useImperativeMethods personaliza el valor de la instancia que se pasa del padre y usa ref directamente. Como siempre, se deben evitar los enlaces directos y se debe utilizar forwardRef


 function FancyInput(props, ref) { const inputRef = useRef(); useImperativeMethods(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} ... />; } FancyInput = forwardRef(FancyInput); 

En este ejemplo, el componente que FancyInput puede llamar a fancyInputRef.current.focus() .


useMutationEffect


useMutationEffect muy similar a useEffect excepto que comienza sincrónicamente en la etapa en que la reacción cambia los valores DOM antes de que se actualicen los componentes vecinos, este enlace debe usarse para realizar mutaciones DOM.
Es mejor preferir useEffect para evitar el bloqueo de los cambios visuales.


useLayoutEffect


useLayoutEffect es similar a useEffect excepto que se ejecuta sincrónicamente después de todas las actualizaciones DOM y la representación sincrónica. Las actualizaciones planificadas en useLayoutEffect se aplican sincrónicamente antes de que el navegador pueda dibujar elementos. También debe intentar usar el efecto de uso estándar para no bloquear los cambios visuales.


useReducer


useReducer es un gancho para crear un reductor que devuelve el estado y la capacidad de enviar cambios:


 const [state, dispatch] = useReducer(reducer, initialState); 

Si comprende cómo funciona Redux, entonces comprende cómo funciona useReducer . El mismo ejemplo que estaba con el contador anterior solo a través de useReducer :


 const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'reset': return initialState; case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; } } function Counter({initialCount}) { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'reset'})}> Reset </button> <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); } 

UseReducer también toma 3 argumentos, esta es la action que debe ejecutarse cuando se inicializa el reductor:


 const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'reset': return {count: action.payload}; case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; } } function Counter({initialCount}) { const [state, dispatch] = useReducer( reducer, initialState, {type: 'reset', payload: initialCount}, ); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'reset', payload: initialCount})}> Reset </button> <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); } 

También podemos crear un contexto en este reductor y usarlo a través del gancho useContext usarlo en toda la aplicación, esto queda para la tarea.


Para resumir


Los ganchos son un enfoque bastante poderoso para resolver el infierno envolvente y resolver varios problemas, pero todos pueden usarse con una definición de transferencia de enlace . Ya ahora comienzan a aparecer colecciones de ganchos para su uso o esta colección . Puede obtener más información sobre ganchos en la documentación .

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


All Articles