Escribir código limpio es una habilidad que se vuelve obligatoria en una determinada etapa de la carrera de un programador. Esta habilidad es especialmente importante cuando el programador intenta encontrar su primer trabajo. Esto, en esencia, es lo que hace que el desarrollador sea un jugador de equipo, y algo que puede "llenar" la entrevista o ayudarlo a pasar con éxito. Los empleadores, al tomar decisiones de personal, miran el código escrito por sus empleados potenciales. El código que escribe el programador debe ser entendido no solo por las máquinas, sino también por las personas.

El material, la primera parte de la traducción que publicamos hoy, presenta consejos para escribir código limpio para aplicaciones React. La relevancia de estos consejos es cuanto mayor, mayor es el tamaño del proyecto en el que se aplican los principios establecidos en ellos. En proyectos pequeños, probablemente pueda hacerlo sin aplicar estos principios. Al decidir qué se necesita en cada situación particular, vale la pena guiarse por el sentido común.
1. Propiedades de desestructuración
Las propiedades de desestructuración (en la terminología React English se llaman "accesorios") es una buena manera de hacer que el código sea más limpio y mejorar sus capacidades de soporte. El hecho es que esto le permite expresar o declarar claramente lo que usa una entidad (como el componente Reaccionar). Sin embargo, este enfoque no obliga a los desarrolladores a leer la implementación del componente para descubrir la composición de las propiedades asociadas con él.
Las propiedades de desestructuración también permiten al programador establecer sus valores predeterminados. Esto es bastante común:
import React from 'react' import Button from 'components/Button' const MyComponent = ({ placeholder = '', style, ...otherProps }) => { return ( <Button type="button" style={{ border: `1px solid ${placeholder ? 'salmon' : '#333'}`, ...style, }} {...otherProps} > Click Me </Button> ) } export default MyComponent
Una de las consecuencias más agradables del uso de la desestructuración en JavaScript, que pude encontrar, es que le permite admitir varias opciones de parámetros.
Por ejemplo, tenemos una función de
authenticate
, que tomó como parámetro el
token
utilizado para autenticar a los usuarios. Más tarde fue necesario hacer que aceptara la entidad
jwt_token
. Esta necesidad fue causada por un cambio en la estructura de la respuesta del servidor. Gracias al uso de la desestructuración, puede organizar fácilmente el soporte para ambos parámetros sin tener que lidiar con la necesidad de cambiar la mayor parte del código de función:
La
jwt_token
se evaluará cuando el código llegue al
token
. Como resultado, si
jwt_token
resulta ser un token válido y la entidad del
token
resulta ser
undefined
, el valor de
jwt_token
caerá en el
token
. Si en el
token
ya había algún valor que no era falso según las reglas de JS (es decir, algún token real), entonces en el
token
simplemente habrá lo que ya está allí.
2. Coloque los archivos componentes en una estructura de carpetas bien pensada
Eche un vistazo a la siguiente estructura de directorios:
- src
- componentes
- Breadcrumb.js
- CollapsedSeparator.js
- De entrada
- index.js
- Input.js
- utils.js
- focusManager.js
- Tarjeta
- index.js
- Card.js
- CardDivider.js
- Button.js
- Typography.js
Las migas de pan pueden incluir separadores. El componente
CollapsedSeparator
se importa en el archivo
Breadcrumb.js
. Esto nos da conocimiento de que en la implementación del proyecto en cuestión están conectados. Sin embargo, alguien que no posee esta información puede sugerir que
Breadcrumb
y
CollapsedSeparator
son un par de componentes completamente independientes que no están conectados entre sí de ninguna manera. Especialmente: si el
CollapsedSeparator
no
CollapsedSeparator
signos claros de que este componente esté asociado con el componente
Breadcrumb
. Entre estos signos, por ejemplo, puede haber un prefijo
Breadcrumb
, usado en el nombre del componente, que puede convertir el nombre en algo así como
BreadcrumbCollapsedSeparator.js
.
Como sabemos que
Breadcrumb
y
CollapsedSeparator
están relacionados entre sí, podemos preguntarnos por qué no se colocan en una carpeta separada, como
Input
y
Card
. Al hacerlo, podemos comenzar a hacer varias suposiciones sobre por qué los materiales del proyecto tienen tal estructura. Digamos, aquí puede pensar en qué componentes se colocaron en el nivel superior del proyecto para ayudarlos a encontrarlos rápidamente, cuidando a quienes trabajarán con el proyecto. Como resultado, la relación entre las partes del proyecto parece bastante vaga para el nuevo desarrollador. El uso de técnicas de escritura de código limpio debería tener exactamente el efecto contrario. El punto es que gracias a ellos, el nuevo desarrollador tiene la oportunidad de leer el código de otra persona y comprender instantáneamente la esencia de la situación.
Si usamos una estructura de directorios bien pensada en nuestro ejemplo, obtenemos algo como lo siguiente:
- src
- Pan rallado
- index.js
- Breadcrumb.js
- CollapsedSeparator.js
- De entrada
- index.js
- Input.js
- utils.js
- focusManager.js
- Tarjeta
- index.js
- Card.js
- CardDivider.js
- Button.js
- Typography.js
Ahora no importa cuántos componentes asociados con el componente
Breadcrumb
se crearán. Mientras sus archivos estén ubicados en el mismo directorio que
Breadcrumb.js
, sabremos que están asociados con el componente
Breadcrumb
:
- src
- Pan rallado
- index.js
- Breadcrumb.js
- CollapsedSeparator.js
- Expander.js
- BreadcrumbText.js
- Breadcrumbhothotog.js
- Breadcrumbfishes.js
- Breadcrumbleftft.js
- Breadcrumbhead.js
- Breadcrumbaddict.js
- Breadcrumbdragon0814.js
- Breadcrumbcontext.js
- De entrada
- index.js
- Input.js
- utils.js
- focusManager.js
- Tarjeta
- index.js
- Card.js
- CardDivider.js
- Button.js
- Typography.js
Así es como se ve trabajar en estructuras similares en código:
import React from 'react' import Breadcrumb, { CollapsedSeparator, Expander, BreadcrumbText, BreadcrumbHotdog, BreadcrumbFishes, BreadcrumbLeftOvers, BreadcrumbHead, BreadcrumbAddict, BreadcrumbDragon0814, } from '../../../../../../../../../../components/Breadcrumb' const withBreadcrumbHotdog = (WrappedComponent) => (props) => ( <WrappedComponent BreadcrumbHotdog={BreadcrumbHotdog} {...props} /> ) const WorldOfBreadcrumbs = ({ BreadcrumbHotdog: BreadcrumbHotdogComponent, }) => { const [hasFishes, setHasFishes] = React.useState(false) return ( <BreadcrumbDragon0814 hasFishes={hasFishes} render={(results) => ( <BreadcrumbFishes> {({ breadcrumbFishes }) => ( <BreadcrumbLeftOvers.Provider> <BreadcrumbHotdogComponent> <Expander> <BreadcrumbText> <BreadcrumbAddict> <pre> <code>{JSON.stringify(results, null, 2)}</code> </pre> </BreadcrumbAddict> </BreadcrumbText> </Expander> {hasFishes ? breadcrumbFishes.map((fish) => ( <> {fish} <CollapsedSeparator /> </> )) : null} </BreadcrumbHotdogComponent> </BreadcrumbLeftOvers.Provider> )} </BreadcrumbFishes> )} /> ) } export default withBreadcrumbHotdog(WorldOfBreadcrumbs)
3. Nombrar componentes usando convenciones de nomenclatura estándar
El uso de ciertos estándares al nombrar componentes facilita que alguien que no sea el autor del proyecto lea el código de este proyecto.
Por ejemplo, los nombres de
componentes de orden superior (HOC) generalmente tienen el prefijo. Muchos desarrolladores están acostumbrados a estos nombres de componentes:
import React from 'react' import hoistNonReactStatics from 'hoist-non-react-statics' import getDisplayName from 'utils/getDisplayName' const withFreeMoney = (WrappedComponent) => { class WithFreeMoney extends React.Component { giveFreeMoney() { return 50000 } render() { return ( <WrappedComponent additionalMoney={[ this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), ]} {...this.props} /> ) } } WithFreeMoney.displayName = `withFreeMoney(${getDisplayName( WrappedComponent, )}$)` hoistNonReactStatics(WithFreeMoney, WrappedComponent) return WithFreeMoney } export default withFreeMoney
Supongamos que alguien decide retirarse de esta práctica y hacer esto:
import React from 'react' import hoistNonReactStatics from 'hoist-non-react-statics' import getDisplayName from 'utils/getDisplayName' const useFreeMoney = (WrappedComponent) => { class WithFreeMoney extends React.Component { giveFreeMoney() { return 50000 } render() { return ( <WrappedComponent additionalMoney={[ this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), ]} {...this.props} /> ) } } WithFreeMoney.displayName = `useFreeMoney(${getDisplayName( WrappedComponent, )}$)` hoistNonReactStatics(WithFreeMoney, WrappedComponent) return WithFreeMoney } export default useFreeMoney
Este es un código JavaScript perfectamente funcional. Los nombres aquí están compuestos, desde un punto de vista técnico, correcto. Pero el prefijo de
use
se
use
habitualmente en otras situaciones, especialmente al nombrar
ganchos React . Como resultado, si alguien escribe un programa que planean mostrar a otra persona, debe tener cuidado con los nombres de las entidades. Esto es especialmente cierto para aquellos casos en que alguien pide ver su código y ayudarlo a resolver un problema. El hecho es que alguien que lee el código de otra persona, posiblemente, ya está acostumbrado a un determinado esquema de denominación de entidades.
Las desviaciones de los estándares generalmente aceptados dificultan la comprensión del código de otra persona.
4. Evita las trampas booleanas
El programador debe actuar con extrema precaución en el caso de que alguna salida dependa de algunos valores lógicos primitivos, y algunas decisiones se tomen en base al análisis de estos valores. Esto sugiere la mala calidad del código. Esto obliga a los desarrolladores a leer el código para implementar componentes u otros mecanismos para tener una idea precisa de cuál es exactamente el resultado de estos mecanismos.
Supongamos que creamos un componente de
Typography
que puede aceptar las siguientes opciones:
'h1'
,
'h2'
,
'h3'
,
'h4'
,
'h5
',
'h6'
,
'title'
,
'subheading'
.
¿Qué afectará exactamente la salida de un componente si las opciones se le pasan de la siguiente forma?
const App = () => ( <Typography color="primary" align="center" subheading title> Welcome to my bio </Typography> )
Aquellos que tienen experiencia con React (o, mejor dicho, con JavaScript) ya pueden asumir que la opción de
title
subheading
opción de
subheading
debido a la forma en que funciona el sistema. La última opción sobrescribirá la primera.
Pero el problema aquí es que no podemos, sin mirar el código, decir exactamente en qué medida se aplicará la opción de
title
o la opción de
subheading
.
Por ejemplo:
.title { font-size: 1.2rem; font-weight: 500; text-transform: uppercase; } .subheading { font-size: 1.1rem; font-weight: 400; text-transform: none !important; }
Aunque el
title
gane, la regla de
text-transform: uppercase
CSS
text-transform: uppercase
no se aplicará. Esto se debe a la mayor especificidad de la
text-transform: none !important
Regla
text-transform: none !important
que existe en el
subheading
. Si no tiene precaución en tales situaciones, depurar tales errores en los estilos puede ser extremadamente difícil. Especialmente, en casos donde el código no muestra algunas advertencias o mensajes de error en la consola. Esto puede complicar la firma del componente.
Una posible solución a este problema es usar una versión más limpia del componente
Typography
:
const App = () => <Typography variant="title">Welcome to my bio</Typography>
Aquí está el código del componente de
Typography
:
import React from 'react' import cx from 'classnames' import styles from './styles.css' const Typography = ({ children, color = '#333', align = 'left', variant, ...otherProps }) => { return ( <div className={cx({ [styles.h1]: variant === 'h1', [styles.h2]: variant === 'h2', [styles.h3]: variant === 'h3', [styles.h4]: variant === 'h4', [styles.h5]: variant === 'h5', [styles.h6]: variant === 'h6', [styles.title]: variant === 'title', [styles.subheading]: variant === 'subheading', })} > {children} </div> ) }
Ahora, cuando en el componente
App
pasamos al componente
Typography
variant="title"
, podemos estar seguros de que solo el
title
afectará la salida del componente. Esto nos ahorra tener que analizar el código del componente para entender cómo se verá este componente.
Para trabajar con propiedades, puede usar la
if/else
simple
if/else
:
let result if (variant === 'h1') result = styles.h1 else if (variant === 'h2') result = styles.h2 else if (variant === 'h3') result = styles.h3 else if (variant === 'h4') result = styles.h4 else if (variant === 'h5') result = styles.h5 else if (variant === 'h6') result = styles.h6 else if (variant === 'title') result = styles.title else if (variant === 'subheading') result = styles.subheading
Pero la principal fortaleza de este enfoque es que simplemente puede usar el siguiente diseño limpio de una sola línea y ponerle fin:
const result = styles[variant]
5. Use las funciones de flecha
Las funciones de flecha representan un mecanismo conciso y claro para declarar funciones en JavaScript (en este caso, sería más correcto hablar sobre la ventaja de las funciones de flecha sobre las expresiones funcionales).
Sin embargo, en algunos casos, los desarrolladores no usan funciones de flecha en lugar de expresiones funcionales. Por ejemplo, cuando es necesario organizar la elevación de funciones.
React utiliza estos conceptos de manera similar. Sin embargo, si un programador no está interesado en aumentar funciones, entonces, en mi opinión, tiene sentido usar la sintaxis de las funciones de flecha:
Cabe señalar que, analizando este ejemplo, es difícil ver las fortalezas de las funciones de flecha. Su belleza se manifiesta completamente cuando se trata de diseños simples de una sola línea:
Estoy seguro de que tales diseños de una sola línea atraerán a todos.
6. Coloque funciones independientes fuera de sus propios ganchos
He visto cómo algunos programadores declaran funciones dentro de sus propios enlaces, pero estos enlaces no necesitan tales funciones en particular. Este tipo de "infla" ligeramente el código de enlace y complica su lectura. Las dificultades para leer el código surgen debido al hecho de que sus lectores pueden comenzar a hacer preguntas sobre si el gancho realmente depende de la función que está dentro de él. Si este no es el caso, es mejor mover la función fuera del gancho. Esto le dará al lector de código una comprensión clara de lo que depende el gancho y de lo que no.
Aquí hay un ejemplo:
import React from 'react' const initialState = { initiated: false, images: [], } const reducer = (state, action) => { switch (action.type) { case 'initiated': return { ...state, initiated: true } case 'set-images': return { ...state, images: action.images } default: return state } } const usePhotosList = ({ imagesList = [] }) => { const [state, dispatch] = React.useReducer(reducer, initialState) const removeFalseyImages = (images = []) => images.reduce((acc, img) => (img ? [...acc, img] : acc), []) React.useEffect(() => { const images = removeFalseyImages(imagesList) dispatch({ type: 'initiated' }) dispatch({ type: 'set-images', images }) }, []) return { ...state, } } export default usePhotosList
Si analizamos este código, podemos entender que las funciones
removeFalseyImages
, de hecho, no tienen que estar presentes dentro del gancho; no interactúa con su estado, lo que significa que se puede colocar fuera del gancho y se puede llamar desde el gancho sin ningún problema.
7. Sea consistente al escribir código
Un enfoque consistente para escribir código es algo que a menudo se recomienda para aquellos que programan en JavaScript.
En el caso de React, vale la pena prestar atención a un enfoque coherente para el uso de los siguientes diseños:
- Equipos de importación y exportación.
- Nombramiento de componentes, ganchos, componentes de orden superior, clases.
Al importar y exportar componentes, a veces uso algo similar a lo siguiente:
import App from './App' export { default as Breadcrumb } from './Breadcrumb' export default App
Pero también me gusta la sintaxis:
export { default } from './App' export { default as Breadcrumb } from './Breadcrumb'
Independientemente de lo que elija el programador, debe usarlo de manera consistente en cada proyecto que cree. Esto simplifica el trabajo de este programador y la lectura de su código por parte de otras personas.
Es muy importante cumplir con las convenciones de nomenclatura de entidades.
Por ejemplo, si alguien le dio al gancho el nombre
useApp
, es importante que los nombres de otros ganchos se construyan de manera similar, usando el prefijo de uso. Por ejemplo, el nombre de otro gancho con este enfoque puede verse como
useController
.
Si no cumple con esta regla, el código de un proyecto, al final, puede ser algo como esto:
Así es como se ve la importación de estos ganchos:
import React from 'react' import useApp from './useApp' import basicController from './basicController' const App = () => { const app = useApp() const controller = basicController() return ( <div> {controller.errors.map((errorMsg) => ( <div>{errorMsg}</div> ))} </div> ) } export default App
A primera vista, es completamente obvio que el
basicController
es un gancho, lo mismo que
useApp
. Esto obliga al desarrollador a leer el código de implementación de lo que importa. Esto se hace solo para comprender con qué se enfrenta exactamente el desarrollador. Si nos adherimos constantemente a la misma estrategia para nombrar entidades, entonces tal situación no surgirá. Todo quedará claro de un vistazo:
const app = useApp() const controller = useBasicController()
Continuará ...
Estimados lectores! ¿Cómo aborda el nombramiento de entidades en sus proyectos React?
