Reducir, reutilizar, reaccionar

Una guía para principiantes para convertir esa pequeña pieza de código reutilizable en su propio paquete npm.

NOTA: Esta es una publicación cruzada que apareció originalmente en JavaScript en enero .

Foto de Gary Chan en Unsplash

Fondo

Hace un tiempo, mientras escribía una aplicación React que llegaba a un punto final REST, escribí una pequeña utilidad para construir una ruta Uri codificada correctamente. En ese momento, no pensé mucho sobre eso. Hizo el trabajo y seguí adelante.

Meses después, mientras trabajaba en otro proyecto, necesitaba algo similar. Me puse a buscarlo y copiar el código, pero no recuerdo dónde lo escribí originalmente.

Después de una buena búsqueda, tanto en mi sistema de archivos local como en GitHub, finalmente lo localicé. Juraba que esto nunca volvería a suceder.

Reducir

Como mencioné, la utilidad crea un camino Uri. Por ejemplo, dado un recurso de “user” y una ID de usuario de “123” , podría devolver una ruta a un recurso de usuario específico de la siguiente manera: "/users/123" .

El lector casual podría estar diciendo: "¿Por qué no puedes concatenar el userId de userId hasta el final de la cadena "/users/" y llamarlo un día?" Tal vez algo como esto.

 const resource = 'usuarios'; 
const userId = '123';
const path = `/ $ {resource} / $ {userId}`;

Eso estaría bien si pudieras jurar en la tumba de tu madre que el nombre del recurso o el ID de usuario no contenía ningún carácter que tuviera que estar codificado en Uri, no solo hoy, sino para siempre.

¿Qué userId si los valores de userId no son solo números, sino identificadores, como "bob" , "jim" , etc. ¿Y qué userId si hay un identificador como "this&that" ? Esto generaría una ruta de "/users/this&that" que es una URL inválida.

¿O qué userId si, por ejemplo, el usuario ingresa su userId como "?format=xml" ? path terminaría siendo "/users/?format=xml" , que probablemente devuelva a todos los usuarios en formato XML, no en absoluto lo que estamos esperando.

Podríamos resolver el problema de la codificación Uri en el ejemplo anterior de esta manera.

 const resource = encodeURIComponent ('usuarios'); 
const userId = encodeURIComponent ('123');
const path = `/ $ {resource} / $ {userId}`;

¡Perfecto! Funciona. Pero … eso es mucho esfuerzo cada vez que quieres generar un camino.

Es por eso que se me ocurrió una utilidad que te escondes de tu lógica principal y puedes volver a usarla una y otra vez. Incorpora el muy útil, ya menudo incomprendido, método Array.reduce , que toma una matriz de valores y los reduce a un solo valor. En nuestro caso, tomamos una serie de cadenas y valores, y los reducimos a una sola cadena.

 const buildUriPath = (cadenas, ... valores) => ( 
strings.reduce ((partialUri, cadena, i) => (
`$ {partialUri} $ {encodeURIComponent (values ??[i - 1])} $ {string}`
)
);

Úselo como se muestra aquí y todo el trabajo de codificación de cada variable se abstrae.

 const resource = 'usuarios'; 
const userId = '123';
const path = buildUriPath` / $ {resource} / $ {userId} `;

En resumen, se necesitan dos matrices: strings (las constantes de cadena ['/', '/', ''] ) y values (los valores de cadena con plantilla que necesitan ser codificados ['users', '123'] ). Construye y devuelve una sola cadena "/users/123" .

OK, eso no es del todo exacto. Se pasa una serie de cadenas, pero lo que estoy llamando una matriz de valores, es en realidad una cantidad variable de argumentos. Estoy usando la sintaxis de "descanso" de ES6 para convertir los argumentos en una matriz de valores.

Cuando reducimos las cadenas, el elemento zeroth de la matriz de cadenas se pasa como partialUri y la iteración comienza con el primer elemento, y así sucesivamente.

No hay paréntesis detrás de la llamada a buildUriPath ? ¿Qué es esta brujería? Esto se llama literal de la plantilla etiquetada. Es una forma especial del literal de plantilla de ES6 que utilizamos anteriormente, pero nos permite procesarlo con una función.

Recuerde que no llamamos a nuestro código directamente. Es invocado por la función literal de la plantilla de JavaScript después de analizar la plantilla en elementos separados.

Los fanáticos de los componentes con estilo ya están familiarizados con esta sintaxis. Es una adición muy poderosa a la especificación ES6.

Reutilizar

Así que ahora que tengo una función buildUriPath super-duper, handy-dandy, vamos a compartirla con el mundo para que todos puedan usarla. Pero el asunto es … Incluso si nadie más quisiera usar esto, igual quiero poder usarlo yo mismo, una y otra vez fácilmente. Podemos hacer esto haciendo un paquete npm. Estoy bastante interesado en el nombre build-uri-path , así que espero que esté disponible … ¡y lo es!

Debido a que escribimos nuestra utilidad en ES6, pero el mundo aún confía en ES5, usaremos Babel para transpilarlo para nosotros.

La fuente completa de todo lo descrito a continuación puede encontrarse en mi repositorio de GitHub .

GitHub

Primero crea una cuenta de GitHub (si aún no tienes una) y luego crea un repositorio en blanco. Clonalo en tu disco local y cambia al directorio. Ejecute npm init para crear un esqueleto package.json . Asegúrese de establecer el punto de entrada en lib/index.js . Esto agregará lo siguiente a su archivo package.json , que le indica al consumidor de su paquete qué archivo ejecutar inicialmente.

 "main": "lib / index.js", 

Instalar Babel

Escribiremos nuestro código en ES6, pero la realidad es que los navegadores actuales no son totalmente compatibles con la sintaxis en la que escribimos. Afortunadamente para todos nosotros, existe una utilidad llamada Babel que nos permite transpilar la fuente ES6 en código distribuible ES5. .

Para instalar Babel, ejecute lo siguiente en un terminal de su elección.

 $ npm install -D babel-cli babel-preset-env 

También necesitarás crear un archivo .babelrc . Esto le dice a Babel cómo transpilar.

 { 
"preajustes": ["env"]
}

Obtener codificación!

Abra su editor favorito y cree una carpeta src con un archivo index.js . Aquí es donde buildUriPath nuestra función buildUriPath que se exportará de manera default .

Toda la fuente se ve así.

 const buildUriPath = (cadenas, ... valores) => ( 
strings.reduce ((partialUri, cadena, i) => (
`$ {partialUri} $ {encodeURIComponent (values ??[i - 1])} $ {string}`
)
);
 exportar por defecto buildUriPath; 

Construir

Ejecute npm run build y debería ver a Babel construir un archivo lib/index.js . Eche un vistazo al interior de este archivo si desea ver el código transpilado ES5. Tenga en cuenta que es sustancialmente más detallado que su fuente ES6. Este es el azúcar sintáctico que trae ES6 a la mesa.

Para paquetes más grandes, considere usar un paquete como rollup.js , pero para nuestro pequeño paquete, publicar el código ES5 debería estar bien.

Pruebas

Como desarrollador, debe demostrarle al mundo, y a usted mismo, que su código funciona bajo una amplia variedad de valores de entrada al escribir muchas pruebas. También son críticos para la regresión .

La prueba es un artículo completo en sí mismo. Puede leer más sobre las pruebas de JavaScript en el artículo de enero de enero JavaScript " Pero en realidad, ¿qué es una prueba de JavaScript? "

Sin embargo, puede ver que las pruebas pasan para nuestro paquete ejecutando la npm test .

Integración continua

Debería considerar configurar la integración continua (CI) como CircleCI o Travis . Las pruebas se ejecutarán automáticamente en cada inserción para su repositorio de código. Esto ayudará a garantizar la calidad del código cuando tenga múltiples contribuidores de código fuente.

Una vez más, CI está más allá del alcance de este artículo, pero puede encontrar una explicación más completa de la configuración de Travis CI aquí .

Linting

La configuración de un linter, como ESLint , también lo ayudará a reducir los errores detectando problemas antes de compilar y probar. Es posible que desee considerar el uso de la popular configuración de ESLint de Airbnb .

Guiones

Tendremos que agregar algunas secuencias de comandos a nuestro archivo package.json para automatizar las cosas. Agregue un comando de build para permitirnos transpilar manualmente nuestro código. Agregue un prepublishOnly hook para que podamos estar seguros de que las pruebas pasen y nuestro código se transmita automáticamente cada vez que ejecute npm publish .

Nuestra sección de scripts se ve así.

 "guiones": { 
"prepublishOnly": "prueba npm && npm run build",
"compilación": "babel src --out-dir lib --ignore '** / *. test.js'",
"prueba": "eslint src && jest"
},

Soporte de TypeScript

Si realmente desea impresionar, puede agregar opcionalmente un archivo de definición de tipo TypeScript (el denominado archivo d.ts ) para que los usuarios de TypeScript puedan tener soporte de tipo de primera clase.

Simplemente cree los types/build-uri-path.d.ts archivo types/build-uri-path.d.ts , en nuestro caso. El archivo se vería así.

 declarar el módulo "build-uri-path" { 
function buildEncodedUri (
cadenas: cadena [],
... valores: cadena []
): cuerda;
exportar por defecto buildEncodedUri;
}

También necesita hacer referencia al archivo desde su archivo package.json agregando la siguiente línea.

 "tipos": "types / build-uri-path.d.ts", 

Consulte la documentación de TypeScript para obtener detalles completos.

Y finalmente … Publicar en npm

Todo lo que queda por hacer es publicar en npm. Pero primero debe ser un usuario en el registro npm. Si no tiene una cuenta, cree una con npm adduser . Una vez que se ha creado un usuario npm, simplemente escriba npm publish . Se ejecutará el script prepublish , que prueba y compila, y si tiene éxito, ¡habrá publicado su primer paquete npm, amigo mío!

Reaccionar

Ahora cualquiera puede usar su nuevo paquete brillante. Entonces, ¿por qué no somos los primeros? Usaré CodeSandbox para instalar build-uri-path y escribiré una aplicación React simple basada en el patrón de carpeta de componentes que la usa.

La aplicación llega a un punto final REST para recuperar algunos datos. Estoy usando Star Wars API como mi back-end. La aplicación frontal React permite al usuario escribir un recurso y una ID. Como estos provienen directamente de una fuente no confiable (es decir, el usuario), necesitan ser codificados. ¡Qué bueno que hay un paquete npm que hará exactamente eso!

Puede ver la aplicación completa, con el código fuente, ejecutándose en CodeSandbox.io .

Puede ver la aplicación completa, con el código fuente, ejecutándose en CodeSandbox.io .

El archivo loadData.js importa nuestra función buildUriPath y la usa para construir la ruta para enviar a la API REST de back-end.

 importar buildUriPath desde 'build-uri-path'; 
...
const path = buildUriPath` / $ {resource} / $ {id} `;

Conclusión

No tiene que escribir el siguiente React para contribuir con un paquete a la comunidad Open Source. Si ves valor en tu trabajo, entiéndelo. Incluso si usted es el único que se beneficia, esa es una razón suficiente.

Pero puede que te sorprenda. Otros pueden encontrarlo útil, ¡y su escaso paquete podría ser la próxima sensación de código abierto!