loading...
Cover image for Creando tabs con styled components

Creando tabs con styled components

jhony24 profile image Jhony Vega ・4 min read

Introducción

Styled components es una herramienta que nos permite escribir el código CSS dentro de javascript y crear componentes sin la necesidad de declarar clases dando una mayor flexibilidad a nuestros componentes.

Instalar los paquetes

Solo se necesitará un paquete necesario para desarrollar el componente.
Comenzaremos instalado styled-components

$ npm install styled-components 
Enter fullscreen mode Exit fullscreen mode

Una vez instalado el paquete necesario organizaremos las carpetas de la siguiente manera para mayor comodidad.

Alt Text

Creamos una carpeta llamada Tab en aquí crearemos el código para el desarrollo del componente. En el archivo styles.js tendremos el estilo de cada elemento de nuestro componente.
En el archivo index.jsx se escribirá la lógica necesario para crear el tab component.

Creando los componentes

Comencemos escribiendo el estilo de los elementos de nuestro componente.

// Tab/styles.js
import styled from "styled-components";

export const TabContainer = styled.div`
  display: flex;
  width: 100%;
  background: red;
  align-items: stretch;
`;

const selectedColor = "rgb(30,190,230)";
const defaultColor = "transparent";

export const TabItem = styled.div`
  background-color: white;
  width: 100%;
  padding: 10px;
  cursor: pointer;
  transition: 0.3s;
  border-bottom: 4px solid ${(props) => (
    props.selected ? selectedColor : defaultColor
  )};
`;
Enter fullscreen mode Exit fullscreen mode

Creamos dos componentes con estilo, el primer componente TabContainer solamente posicionará los componentes hijos horizontalmente.
El componente TabItem es el principal, en el cuál queremos que cambie el color del borde inferior si tiene la propiedad selected en verdadero, si es así se asigna un color declarado en la constante selectedColor caso contrario el color del border recibe la constante defaultColor cual tiene un color transparente.

Creando el componente Tab.

//Tab/index.jsx
import React, { useEffect, useState } from "react";
import * as S from "./styles";

const Tab = ({ children, onTabSelected }) => {
  const [itemId, setItemId] = useState(0);

  useEffect(() => {
    onTabSelected && onTabSelected(itemId);
  }, [itemId, onTabSelected]);

  return (
    <S.TabContainer>
      {React.Children.map(children, (child, index) => {
        return React.cloneElement(child, {
          onClick: () => {
            setItemId(index);
          },
          selected: itemId === index
        });
      })}
    </S.TabContainer>
  );
};

export default Tab;
Enter fullscreen mode Exit fullscreen mode

Lo primero que hacemos es importar todos los componentes de estilo con un alias cuál llamaremos S.

import * as S from "./styles";
Enter fullscreen mode Exit fullscreen mode

Usaremos un estado para seleccionar el id actual del item, en este caso usaremos el índice de cada elemento hijo como su id.
Lo siguiente y la parte más importante que se hará es el siguiente código.

return (
  <S.TabContainer>
    {React.Children.map(children, (child, index) => {
      return React.cloneElement(child, {
        onClick: () => {
          setItemId(index);
        },
        selected: itemId === index
      });
    })}
  </S.TabContainer>
);
Enter fullscreen mode Exit fullscreen mode

Lo primero que hacemos es estilizar con el componente , seguidamente usamos el Top-Level API React.children.map el cuál recibe como primer argumento el children, seguidamente una función en la cuál nos devolverá el componente actual y su índice.
Dentro de la función lo que haremos es clonar cada elemento con ayuda de otra API de React, React.cloneElement, su función principal es como su nombre lo indica clonar un elemento el cuál nos devolverá una copia del componente con sus propiedades que se declaren. Asimismo, también podremos asignarles nuevas propiedades en este caso le asignamos un evento onClick y una propiedad selected que como recordemos esta propiedad fué creada en el componente con estilo TabItem y aquí lo estamos manipulando para no tener la necesidad de escribir la lógica fuera de ella.

El evento que se le asigna ejecuta la acción de asignar el índice al estado de la siguiente manera, al momento de asignarle el estado también comparamos si el índice del elemento actual es igual al estado, si es así entonces se le aplica la propiedad selected caso contrario no.

return React.cloneElement(child, {
  onClick: () => {
     setItemId(index);
  },
  selected: itemId === index
});
Enter fullscreen mode Exit fullscreen mode

Creando el subcomponente TabItem.

//Tab/index.jsx
export const TabItem = memo(({ children, ...restProps }) => (
  <S.TabItem {...restProps}>{children}</S.TabItem>
));
Enter fullscreen mode Exit fullscreen mode

Al componente TabItem le agregaremos el componente con estilo . Además le pasaremos la propiedad children y las demás propiedades restantes le asignaremos al componente con estilo cual desde el componente padre automáticamente agregará las funcionalidades requeridas. Como se puede observar está envuelto en componente de orden superior(memo) para evitar la rerenderización.

El código final sería el siguiente.

//Tab/index.jsx
import React, { memo, useEffect, useState } from "react";
import * as S from "./styles";

const Tab = ({ children, onTabSelected }) => {
  const [itemId, setItemId] = useState(0);

  useEffect(() => {
    onTabSelected(itemId);
  }, [itemId, onTabSelected]);

  return (
    <S.TabContainer>
      {React.Children.map(children, (child, index) => {
        return React.cloneElement(child, {
          onClick: () => {
            setItemId(index);
          },
          selected: itemId === index
        });
      })}
    </S.TabContainer>
  );
};

export const TabItem = memo(({ children, ...restProps }) => (
  <S.TabItem {...restProps}>{children}</S.TabItem>
));

export default Tab;
Enter fullscreen mode Exit fullscreen mode

Ahora podemos utilizar el componente de la siguiente manera.

import React from "react";
import Tab, { TabItem } from "./Tab";

export default function App() {
  const onTabSelected = (index) => {
    console.log(index);
  };

  return (
    <div className="App">
      <Tab onTabSelected={onTabSelected}>
        <TabItem>item 1</TabItem>
        <TabItem>item 2</TabItem>
        <TabItem>item 3</TabItem>
      </Tab>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

El resultado final quedaría de la siguiente manera.

Alt Text

Aquí puedes ver el código y un demo

Discussion

pic
Editor guide