Saltar al contenido

c

Más sobre estilos

En la parte 2 examinamos dos formas diferentes de agregar estilos a nuestra aplicación: el archivo CSS único de la vieja escuela y los estilos en línea. En esta parte veremos algunas otras formas.

Librerías de UI listas para usar

Un enfoque para definir estilos para una aplicación es utilizar un "UI framework" listo para usar.

Uno de los primeros frameworks de UI más populares fue el kit de herramientas Bootstrap creado por Twitter, que aún podría ser el más popular. Recientemente ha habido una explosión en la cantidad de nuevos frameworks de UI que han entrado a la arena. De hecho, la selección es tan amplia que hay pocas esperanzas de crear una lista de opciones exhaustiva.

Muchos frameworks de UI proporcionan a los desarrolladores de aplicaciones web temas y "componentes" listos para usar, como botones, menús y tablas. Escribimos los componentes entre comillas, porque en este contexto no estamos hablando de componentes de React. Por lo general, los frameworks de UI se utilizan al incluir las hojas de estilo CSS y el código JavaScript del framework en la aplicación.

Hay muchos frameworks de UI que tienen versiones compatibles con React, donde los "componentes" del framework se han transformado en componentes de React. Hay algunas versiones diferentes de Bootstrap para React como reactstrap y react-bootstrap.

A continuación, analizaremos más de cerca dos frameworks de UI, Bootstrap y MaterialUI. Usaremos ambos para agregar estilos similares a la aplicación que hicimos en la sección React-router del material del curso.

React Bootstrap

Comencemos por echar un vistazo a Bootstrap con la ayuda del paquete react-bootstrap.

Instalemos el paquete con el comando:

npm install react-bootstrap

Luego agreguemos un link para cargar la hoja de estilo CSS para Bootstrap dentro de la etiqueta head en el archivo public/index.html de la aplicación:

<head>
  <link
    rel="stylesheet"
    href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
    integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
    crossorigin="anonymous"
  />
  // ...
</head>

Cuando recargamos la aplicación, notamos que ya se ve un poco más elegante:

aplicación de notas con bootstrap

En Bootstrap, todo el contenido de la aplicación generalmente se renderiza dentro de un container. En la práctica, esto se logra dando al elemento div raíz de la aplicación el atributo de clase container:

const App = () => {
  // ...

  return (
    <div className="container">      // ...
    </div>
  )
}

Notamos que esto ya tiene un efecto en la apariencia de la aplicación. El contenido ya no está tan cerca de los bordes del navegador como antes:

aplicación de notas con espacio de margin

Tablas

A continuación, hagamos algunos cambios en el componente Notes, para que muestre la lista de notas como una tabla. React Bootstrap proporciona un componente Table integrado para este propósito, por lo que no es necesario definir clases CSS por separado.

const Notes = ({ notes }) => (
  <div>
    <h2>Notes</h2>
    <Table striped>      <tbody>
        {notes.map(note =>
          <tr key={note.id}>
            <td>
              <Link to={`/notes/${note.id}`}>
                {note.content}
              </Link>
            </td>
            <td>
              {note.user}
            </td>
          </tr>
        )}
      </tbody>
    </Table>
  </div>
)

La apariencia de la aplicación es bastante elegante:

tab de notas con nueva tabla

Ten en cuenta que los componentes de React Bootstrap deben importarse por separado de la librería como se muestra a continuación:

import { Table } from 'react-bootstrap'

Formularios

Mejoremos el formulario en la vista de inicio de sesión con la ayuda de forms Bootstrap.

React Bootstrap proporciona componentes integrados para crear formularios (aunque su documentación no es muy buena):

let Login = (props) => {
  // ...
  return (
    <div>
      <h2>login</h2>
      <Form onSubmit={onSubmit}>
        <Form.Group>
          <Form.Label>username:</Form.Label>
          <Form.Control
            type="text"
            name="username"
          />
        </Form.Group>
        <Form.Group>
          <Form.Label>password:</Form.Label>
          <Form.Control
            type="password"
          />
        </Form.Group>
        <Button variant="primary" type="submit">
          login
        </Button>
      </Form>
    </div>
  )
}

La cantidad de componentes que necesitamos importar aumenta:

import { Table, Form, Button } from 'react-bootstrap'

Después de cambiar al formulario Bootstrap, nuestra aplicación mejorada se ve así:

aplicación de notas con login de bootstrap

Notificación

Ahora que el formulario de inicio de sesión está en mejor forma, echemos un vistazo a cómo mejorar las notificaciones de nuestra aplicación:

aplicación de notas con notificación de bootstrap

Agreguemos un mensaje para la notificación cuando un usuario inicia sesión en la aplicación. Lo almacenaremos en la variable message en el estado del componente App:

const App = () => {
  const [notes, setNotes] = useState([
    // ...
  ])

  const [user, setUser] = useState(null)
  const [message, setMessage] = useState(null)
  const login = (user) => {
    setUser(user)
    setMessage(`welcome ${user}`)    setTimeout(() => {      setMessage(null)    }, 10000)  }
  // ...
}

Renderizaremos el mensaje como un componente Alert de Bootstrap. Una vez más, la librería React Bootstrap nos proporciona su componente de React correspondiente:

<div className="container">
  {(message &&    <Alert variant="success">      {message}    </Alert>  )}  // ...
</div>

Estructura de navegación

Por último, modifiquemos el menú de navegación de la aplicación para usar el componente Navbar de Bootstrap. La librería React Bootstrap nos proporciona componentes incorporados coincidentes. A través de prueba y error, terminamos con una solución que funciona a pesar de la documentación críptica:

<Navbar collapseOnSelect expand="lg" bg="dark" variant="dark">
  <Navbar.Toggle aria-controls="responsive-navbar-nav" />
  <Navbar.Collapse id="responsive-navbar-nav">
    <Nav className="me-auto">
      <Nav.Link href="#" as="span">
        <Link style={padding} to="/">home</Link>
      </Nav.Link>
      <Nav.Link href="#" as="span">
        <Link style={padding} to="/notes">notes</Link>
      </Nav.Link>
      <Nav.Link href="#" as="span">
        <Link style={padding} to="/users">users</Link>
      </Nav.Link>
      <Nav.Link href="#" as="span">
        {user
          ? <em style={padding}>{user} logged in</em>
          : <Link style={padding} to="/login">login</Link>
        }
      </Nav.Link>
    </Nav>
  </Navbar.Collapse>
</Navbar>

El diseño resultante tiene un aspecto muy limpio y agradable:

barra de navegación negra hecha con bootstrap en la aplicación de notas

Si la ventana gráfica del navegador se reduce, notamos que el menú "colapsa" y se puede expandir haciendo clic en el botón "hamburguesa":

aplicación de notas con menú hamburguesa

Bootstrap y una gran mayoría de los frameworks de UI existentes producen diseños responsive, lo que significa que las aplicaciones resultantes se renderizan bien en una variedad de tamaños de pantalla diferentes.

Las herramientas para desarrolladores de Chrome permiten simular el uso de nuestra aplicación en el navegador de diferentes clientes móviles:

devtools de chrome con vista previa de navegador de móvil de la aplicación de notas

Puedes encontrar el código completo de la aplicación aquí.

Material UI

Como segundo ejemplo, veremos la librería de React MaterialUI, que implementa el lenguaje visual de Material design desarrollado por Google.

Instala la librería con el comando

npm install @mui/material @emotion/react @emotion/styled

Ahora usemos MaterialUI para hacer las mismas modificaciones al código que hicimos anteriormente con Bootstrap.

Renderiza el contenido de toda la aplicación dentro de un Container:

import { Container } from '@mui/material'

const App = () => {
  // ...
  return (
    <Container>
      // ...
    </Container>
  )
}

Tabla

Comencemos con el componente de Notes. Mostraremos la lista de notas como una tabla:

const Notes = ({ notes }) => (
  <div>
    <h2>Notes</h2>

    <TableContainer component={Paper}>
      <Table>
        <TableBody>
          {notes.map(note => (
            <TableRow key={note.id}>
              <TableCell>
                <Link to={`/notes/${note.id}`}>{note.content}</Link>
              </TableCell>
              <TableCell>
                {note.user}
              </TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  </div>
)

La tabla se ve así:

tabla de notas de material UI

Una característica no muy agradable de Material UI es que cada componente debe importarse por separado. La lista de importación para la página de notas es bastante larga::

import {
  Container,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableRow,
  Paper,
} from '@mui/material'

Formulario

A continuación, mejoremos el formulario de inicio de sesión en la vista Login utilizando los componentes TextField y Button:

const Login = (props) => {
  const navigate = useNavigate()

  const onSubmit = (event) => {
    event.preventDefault()
    props.onLogin('mluukkai')
    navigate('/')
  }

  return (
    <div>
      <h2>login</h2>
      <form onSubmit={onSubmit}>
        <div>
          <TextField label="username" />
        </div>
        <div>
          <TextField label="password" type='password' />
        </div>
        <div>
          <Button variant="contained" color="primary" type="submit">
            login
          </Button>
        </div>
      </form>
    </div>
  )
}

El resultado final es:

formulario de login de material UI en la aplicación de notas

MaterialUI, a diferencia de Bootstrap, no proporciona un componente para el formulario en sí. El formulario aquí es un elemento form HTML ordinario.

Recuerda importar todos los componentes utilizados en el formulario.

Notificación

La notificación que se muestra al iniciar sesión se puede hacer usando el componente Alert, que es bastante similar al componente equivalente de Bootstrap:

<div>
  {(message &&    <Alert severity="success">      {message}    </Alert>  )}</div>

La alerta es bastante elegante:

notificaciones de material ui en la aplicación de notas

Estructura de navegación

Podemos implementar la navegación usando el componente AppBar.

Si usamos el código de ejemplo de la documentación

<AppBar position="static">
  <Toolbar>
    <IconButton edge="start" color="inherit" aria-label="menu">
    </IconButton>
    <Button color="inherit">
      <Link to="/">home</Link>
    </Button>
    <Button color="inherit">
      <Link to="/notes">notes</Link>
    </Button>
    <Button color="inherit">
      <Link to="/users">users</Link>
    </Button>  
    <Button color="inherit">
      {user
        ? <em>{user} logged in</em>
        : <Link to="/login">login</Link>
      }
    </Button>                
  </Toolbar>
</AppBar>

tenemos navegación funcional, pero podría verse mejor

navbar azul de material ui en la aplicación de notas

Podemos encontrar una mejor manera en la documentación. Podemos usar component props para definir cómo se renderiza el elemento raíz de un componente MaterialUI.

Definiendo

<Button color="inherit" component={Link} to="/">
  home
</Button>

el componente Button se renderiza de tal manera, que su componente raíz es Link de react-router-dom, el cual recibe su ruta como campo de prop to.

El código para la barra de navegación es el siguiente:

<AppBar position="static">
  <Toolbar>
    <Button color="inherit" component={Link} to="/">
      home
    </Button>
    <Button color="inherit" component={Link} to="/notes">
      notes
    </Button>
    <Button color="inherit" component={Link} to="/users">
      users
    </Button>   
    {user
      ? <em>{user} logged in</em>
      : <Button color="inherit" component={Link} to="/login">
          login
        </Button>
    }                              
  </Toolbar>
</AppBar>

y se ve como queremos:

navbar azul con texto blanco de material ui en aplicación de notas

El código de la aplicación se puede encontrar aquí.

Pensamientos finales

La diferencia entre react-bootstrap y MaterialUI no es grande. Depende de ti cuál te resulte más atractivo. Yo no he usado mucho MaterialUI, pero mis primeras impresiones son positivas. Su documentación es un poco mejor que la de react-bootstrap. Según https://www.npmtrends.com/, que rastrea la popularidad de las diferentes librerías de npm, MaterialUI pasó a react-bootstrap en popularidad a fines de 2018:

npmtrends de materialUI vs bootstrap

En los dos ejemplos anteriores, usamos los frameworks de UI con la ayuda de las librerías de integración de React.

En lugar de usar la librería React Bootstrap, podríamos haber usado Bootstrap directamente definiendo clases CSS para los elementos HTML de nuestra aplicación. En lugar de definir la tabla con el componente Table:

<Table striped>
  // ...
</Table>

Podríamos haber usado una tabla HTML normal y agregar la clase CSS requerida:

<table className="table striped">
  // ...
</table>

El beneficio de usar la librería React Bootstrap no es tan evidente en este ejemplo.

Además de hacer que el código del frontend sea más compacto y legible, otro beneficio de usar las librerías de frameworks de UI de React es que incluyen el JavaScript que se necesita para que los componentes específicos funcionen. Algunos componentes de Bootstrap requieren algunas dependencias de JavaScript desagradables que preferiríamos no incluir en nuestras aplicaciones React.

Algunas posibles desventajas de usar frameworks de UI a través de librerías de integración en lugar de usarlos "directamente" son que las librerías de integración pueden tener API inestables y documentación deficiente. La situación con Semantic UI React es mucho mejor que con muchos otros frameworks de UI, ya que es una librería de integración oficial de React.

También está la cuestión de si las librerías de framework de UI deben usarse en primer lugar. Depende de cada uno formarse su propia opinión, pero para las personas que carecen de conocimientos en CSS y diseño web, son herramientas muy útiles.

Otros frameworks de UI

Aquí hay otros frameworks de UI para tu consideración. Si no ves tu framework de UI favorito en la lista, haz un pull request al material del curso para agregarlo.

Styled components

También hay otras formas de agregarle estilos a aplicaciones React que aún no hemos analizado.

La librería styled components ofrece un enfoque interesante para definir estilos a través de plantillas literales etiquetadas que se introdujeron en ES6.

Hagamos algunos cambios en los estilos de nuestra aplicación con la ayuda de styled components. Primero, instala el paquete con el comando:

npm install styled-components

Luego definamos dos componentes con estilos:

import styled from 'styled-components'

const Button = styled.button`
  background: Bisque;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid Chocolate;
  border-radius: 3px;
`

const Input = styled.input`
  margin: 0.25em;
`

El código de arriba crea versiones con estilo de los elementos HTML button e input y luego los asigna a las variables Button e Input.

La sintaxis para definir los estilos es bastante interesante, ya que las reglas CSS se definen dentro de las comillas invertidas.

Los componentes con estilo que definimos funcionan exactamente como los elementos normales button e input, y se pueden usar de la misma manera:

const Login = (props) => {
  // ...
  return (
    <div>
      <h2>login</h2>
      <form onSubmit={onSubmit}>
        <div>
          username:
          <Input />        </div>
        <div>
          password:
          <Input type='password' />        </div>
        <Button type="submit" primary=''>login</Button>      </form>
    </div>
  )
}

Creemos algunos componentes más para estilizar esta aplicación, que serán versiones con estilo de elementos div:

const Page = styled.div`
  padding: 1em;
  background: papayawhip;
`

const Navigation = styled.div`
  background: BurlyWood;
  padding: 1em;
`

const Footer = styled.div`
  background: Chocolate;
  padding: 1em;
  margin-top: 1em;
`

Usemos los componentes en nuestra aplicación:

const App = () => {
  // ...

  return (
     <Page>      <Navigation>        <Link style={padding} to="/">home</Link>
        <Link style={padding} to="/notes">notes</Link>
        <Link style={padding} to="/users">users</Link>
        {user
          ? <em>{user} logged in</em>
          : <Link style={padding} to="/login">login</Link>
        }
      </Navigation>      
      <Routes>
        <Route path="/notes/:id" element={<Note note={note} />} />  
        <Route path="/notes" element={<Notes notes={notes} />} />   
        <Route path="/users" element={user ? <Users /> : <Navigate replace to="/login" />} />
        <Route path="/login" element={<Login onLogin={login} />} />
        <Route path="/" element={<Home />} />      
      </Routes>

      <Footer>        <em>Note app, Department of Computer Science 2022</em>
      </Footer>    </Page>  )
}

La apariencia de la aplicación resultante se muestra a continuación:

aplicación de notas con styled components

Styled components ha experimentado un crecimiento constante en popularidad en los últimos tiempos, y mucha gente considera que es la mejor forma de definir estilos para las aplicaciones React.