5 mejores prácticas para usar ganchos React en producción

El autor del artículo, cuya traducción publicamos hoy, dice que commercetools adoptó los ganchos React a principios de 2019, en el momento en que aparecieron en React 16.8.0. Desde entonces, los programadores de la compañía han estado procesando constantemente su código, traduciéndolo en ganchos. Los ganchos de reacción le permiten usar el estado del componente y otras características de reacción sin usar clases. Al usar ganchos, puede, cuando trabaje con componentes funcionales, "conectarse" a los eventos del ciclo de vida del componente y responder a los cambios en su estado.



Brevemente sobre los resultados de implementar ganchos


Si en pocas palabras hablamos de lo que nos dieron los ganchos, resulta que ayudaron a simplificar la base del código al extraer la lógica de los componentes y facilitar la composición de diferentes capacidades. Además, el uso de ganchos ha llevado al hecho de que hemos aprendido mucho. Por ejemplo, cómo estructurar mejor su código a través de la mejora continua de la funcionalidad existente. Estamos seguros de que aprenderemos muchas más cosas interesantes mientras continuamos trabajando con los ganchos React.

Fortalezas de la implementación del gancho


Esto es lo que nos dio la introducción de ganchos:

  • Mejora de la legibilidad del código. Esto es posible gracias al uso de árboles de componentes más pequeños que antes. La creación de tales árboles de componentes se ve facilitada por nuestro deseo de abandonar las propiedades de representación y los componentes de orden superior.
  • Mejora de las capacidades de depuración de código. Tuvimos a nuestra disposición una representación visual mejorada del código e información adicional de depuración proporcionada por las herramientas de desarrollador React.
  • Mejora de la modularidad del proyecto. Debido a la naturaleza funcional de los ganchos, se ha simplificado el proceso de creación y aplicación de lógica adecuada para uso repetido.
  • Separación de responsabilidades. Los componentes de reacción son responsables de la apariencia de los elementos de la interfaz, y los ganchos proporcionan acceso a la lógica del programa encapsulado.

Ahora queremos compartir nuestras recomendaciones sobre el uso de ganchos en la producción.

1. Recupere oportunamente los ganchos de los componentes y cree ganchos personalizados


Comenzar a usar ganchos en componentes funcionales es bastante fácil. Por ejemplo, aplicamos rápidamente en algún lugar React.useState, en algún lugar, React.useEffecty continuamos haciendo nuestro trabajo. Sin embargo, este enfoque no permite aprovechar al máximo todas las oportunidades que los ganchos son capaces de brindar. Presentando React.useStateen forma de un pequeño use<StateName>State, y React.useEffect- en la forma use<EffectName>Effect, pudimos lograr lo siguiente:

  1. Los componentes son más pequeños que con ganchos regulares.
  2. Los ganchos se pueden reutilizar en varios componentes.
  3. , React, , . , State<StateName> State. React.useState, .

Recuperar ganchos también contribuye al hecho de que la lógica reutilizable y compartida es más visible en varias partes de la aplicación. La lógica similar o duplicada es más difícil de notar si solo usa ganchos integrados en el código del componente. Los ganchos resultantes pueden ser pequeños y contener muy poco código useToggleState. Por otro lado, los ganchos más grandes, como useProductFetcher, ahora pueden incluir más funcionalidad. Ambos nos ayudaron a simplificar la base del código al reducir el tamaño de los componentes React.

El siguiente ejemplo crea un pequeño gancho React diseñado para controlar la selección de elementos. Los beneficios de encapsular esta funcionalidad se hacen evidentes inmediatamente después de darse cuenta de la frecuencia con la que se usa dicha lógica en la aplicación. Por ejemplo, para seleccionar un conjunto de pedidos de una lista.

//  

function Component() {
    const [selected, setSelected] = React.useState()
    const select = React.useCallback(id => setSelected(/** ... */), [
        selected,
        setSelect
    ])

    return <List selectedIds={selected} onSelect={id => select(id)} />
}

//   

const useSelection = () => {
    const [selected, setSelected] = React.useState()
    const select = React.useCallback(id => setSelected(/** ... */), [
        selected,
        setSelect
    ])

    return [selected, select]
}

function Component() {
    const [selected, select] = useSelection()

    return <List selectedIds={selected} onSelect={id => select(id)} />
}

2. Acerca de los beneficios de React.useDebugValue


El enlace estándar React.useDebugValue se refiere a las características poco conocidas de React. Este enlace puede ayudar al desarrollador durante la depuración del código; puede ser útil para usarlo en enlaces diseñados para compartir. Estos son ganchos de usuario que se utilizan en muchos componentes de la aplicación. Sin embargo, no se useDebugValuerecomienda usarlo en todos los enlaces de usuario, ya que los enlaces integrados ya registran valores de depuración estándar.

Imagine crear un enlace personalizado diseñado para tomar una decisión sobre la necesidad de habilitar alguna función de la aplicación. Los datos del estado de la aplicación en los que se basa la decisión pueden provenir de diferentes fuentes. Se pueden almacenar en un objeto de contexto React, cuyo acceso se organiza a través de él React.useContext.

Para ayudar al desarrollador en la depuración, mientras trabaja con las herramientas de desarrollador React, puede ser útil conocer el nombre del indicador analizado ( flagName) y la variante del valor del indicador ( flagVariation). En este caso, usar un gancho pequeño puede ayudarnos a React.useDebugValue:

export default function useFeatureToggle(flagName, flagVariation = true) {
    const flags = React.useContext(FlagsContext)
    const isFeatureEnabled = getIsFeatureEnabled(flagName, flagVariation)(flags)

    React.useDebugValue({
        flagName,
        flagVariation,
        isEnabled: isFeatureEnabled
    })

    return isFeatureEnabled
}

Ahora, trabajando con las herramientas del desarrollador React, podemos ver información sobre la opción del valor del indicador, sobre el nombre del indicador y si la función que nos interesa está activada o no.


Trabajar con las herramientas de desarrollador React después de aplicar el enlace React.useDebugValue

Tenga en cuenta que en los casos en que los usuarios usan enlaces estándar comoReact.useStateoReact.useRef, dichos enlaces ya registran el estado correspondiente o los datos del objeto de referencia. Como resultado, el uso de estructuras de vista aquíReact.useDebugValue({ state })no es particularmente útil.

3. La combinación y composición de ganchos.


Cuando comenzamos a implementar ganchos en nuestro trabajo y comenzamos a usar más y más ganchos en los componentes, rápidamente resultó que 5-10 ganchos podrían estar presentes en el mismo componente. En este caso, se utilizaron ganchos de varios tipos. Por ejemplo, podríamos usar 2-3 enlaces React.useState, luego un enlace React.useContext(por ejemplo, para obtener información sobre el usuario activo), un enlace React.useEffecty también enlaces de otras bibliotecas, como react-router o react-intl .

Lo anterior se repitió una y otra vez, como resultado, incluso los componentes muy pequeños no eran tan compactos. Para evitar esto, comenzamos a extraer estos ganchos individuales en ganchos de usuario, cuyo dispositivo dependía del componente o alguna capacidad de la aplicación.

Imagine un mecanismo de aplicación destinado a crear un pedido. Al desarrollar este mecanismo, se utilizaron muchos componentes, así como ganchos de varios tipos. Estos ganchos se pueden combinar como un gancho personalizado, lo que aumenta la usabilidad de los componentes. Aquí hay un ejemplo de combinación de un conjunto de ganchos pequeños en un gancho.

function OrderCreate() {
    const {
        orderCreater,
        orderFetcher,
        applicationContext,
        goToNextStep,
        pendingOrder,
        updatePendingChanges,
        revertPendingChanges
    } = useOrderCreate()

    return (/** ...children */)
}

4. Comparación de React.useReducer y React.useState


A menudo recurrimos React.useStatecomo un gancho estándar para trabajar con el estado de los componentes. Sin embargo, con el tiempo, el estado del componente puede necesitar ser complicado, lo que depende de los nuevos requisitos para el componente, como la presencia de varios valores en el estado. En ciertos casos, el uso React.useReducerpuede ayudar a evitar la necesidad de usar múltiples valores y simplificar la lógica de actualización del estado. Imagine la gestión de estado al realizar solicitudes HTTP y recibir respuestas. Para hacer esto, es posible que deba trabajar con valores como isLoading, datayerror. En cambio, el estado puede controlarse utilizando un reductor, teniendo a su disposición varias acciones para gestionar los cambios de estado. Este enfoque, idealmente, alienta al desarrollador a percibir el estado de la interfaz en forma de máquina de estados.

El reductor transmitido React.useReduceres similar a los reductores utilizados en Redux , donde el sistema recibe el estado actual de la acción y debe devolver el siguiente estado. La acción contiene información sobre su tipo, así como datos sobre la base de los cuales se forma el siguiente estado. Aquí hay un ejemplo de un reductor simple diseñado para controlar cierto contador:

const initialState = 0;
const reducer = (state, action) => {
    switch (action) {
        case 'increment': return state + 1;
        case 'decrement': return state - 1;
        case 'reset': return 0;
        default: throw new Error('Unexpected action');
    }
};

Este reductor puede probarse de forma aislada y luego usarse en el componente usando React.useReducer:

function Component() {
    const [count, dispatch] = React.useReducer(reducer, initialState);

    return (
        <div>
            {count}
            <button onClick={() => dispatch('increment')}>+1</button>
            <button onClick={() => dispatch('decrement')}>-1</button>
            <button onClick={() => dispatch('reset')}>reset</button>
        </div>
    );
};

Aquí podemos aplicar lo que examinamos en las tres secciones anteriores, a saber, podemos extraer todo en useCounterReducer. Esto mejorará el código al ocultar la información del tipo de acción de un componente que describe la apariencia de un elemento de interfaz. Como resultado, esto ayudará a evitar la filtración de detalles de implementación en el componente y también nos dará oportunidades adicionales para depurar el código. Así se verá el gancho personalizado resultante y el componente que lo usa:

const CounterActionTypes = {
    Increment: 'increment',
    Decrement: 'decrement',
    Reset: 'reset',
}

const useCounterReducer = (initialState) => {
    const [count, dispatch] = React.useReducer(reducer, initialState);

    const increment = React.useCallback(() => dispatch(CounterActionTypes.Increment));
    const decrement = React.useCallback(() => dispatch(CounterActionTypes.Decrement));
    const reset = React.useCallback(() => dispatch(CounterActionTypes.Reset));

    return {
        count,
        increment,
        decrement
    }
}

function Component() {
    const {count, increment} = useCounterReducer(0);

    return (
        <div>
            {count}
            <button onClick={increment}>+1</button>
        </div>
    );
};

5. Implementación gradual de ganchos


A primera vista, la idea de introducir ganchos gradualmente puede no parecer completamente lógica, pero aquí sugiero que aquellos que piensan así simplemente sigan mi razonamiento. Con el tiempo, varios patrones encuentran aplicación en la base de código. En nuestro caso, estos patrones incluyen componentes de orden superior, propiedades de representación y ahora ganchos. Al traducir un proyecto a un nuevo patrón, los desarrolladores no buscan reescribir instantáneamente todo el código, lo que, en general, es casi imposible. Como resultado, debe desarrollar un plan para transferir el proyecto a React hooks, que no incluye cambios importantes en el código. Esta tarea puede ser muy difícil, ya que realizar cambios en el código generalmente conduce a un aumento en su tamaño y complejidad. Al introducir ganchos, nos esforzamos por evitar esto.

Nuestra base de código utiliza componentes basados ​​en clases y componentes funcionales. Independientemente de qué componente se utilizó en una situación determinada, buscamos compartir la lógica a través de los ganchos React. Primero, implementamos la lógica en ganchos (o repetimos la implementación existente de ciertos mecanismos en ellos), luego creamos pequeños componentes de un orden superior, dentro de los cuales se usan estos ganchos. Después de eso, estos componentes de orden superior se utilizan para crear componentes basados ​​en clases. Como resultado, tenemos a nuestra disposición la lógica ubicada en un solo lugar, que puede usarse en componentes de diversos tipos. Aquí hay un ejemplo de implementación de la funcionalidad de enlace en componentes a través de componentes de orden superior.

export const injectTracking = (propName = 'tracking') => WrappedComponent => {
    const WithTracking = props => {
        const tracking = useTracking();

        const trackingProp = {
            [propName]: tracking,
        };

        return <WrappedComponent {...props} {...trackingProp} />;
    };

    WithTracking.displayName = wrapDisplayName(WrappedComponent, 'withTracking');

    return WithTracking;
};

export default injectTracking;

Esto muestra la implementación de la funcionalidad de enlace useTrackingen un componente WrappedComponent. Esto, por cierto, nos permitió, entre otras cosas, separar las tareas de implementar enlaces y reescribir pruebas en las partes antiguas del sistema. Con este enfoque, todavía tenemos a nuestra disposición mecanismos para usar ganchos en todas las partes de la base de código.

Resumen


Aquí hay algunos ejemplos de cómo el uso de ganchos React mejoró nuestra base de código. Esperamos que los ganchos también puedan beneficiar a su proyecto.

¡Queridos lectores! ¿Utilizas ganchos React?


All Articles