Creando tabs con styled components
Tags: react, css, styled, tutorial
Published: 2020-11-19 00:01:26

# 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**

```console
$ npm install styled-components 
```
Una vez instalado el paquete necesario organizaremos las carpetas de la siguiente manera para mayor comodidad.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/6zugtev6okfkh1sjxjj7.PNG)

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. 

```javascript
// 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
  )};
`;
```

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**.
```jsx
//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;
```

Lo primero que hacemos es importar todos los componentes de estilo con un alias cuál llamaremos **S**. 
```javascript 
import * as S from "./styles";
```

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.

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

Lo primero que hacemos es estilizar con el componente **</S.TabContainer>**, 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.

```jsx
return React.cloneElement(child, {
  onClick: () => {
     setItemId(index);
  },
  selected: itemId === index
});
``` 

Creando el subcomponente **TabItem**.
```jsx
//Tab/index.jsx
export const TabItem = memo(({ children, ...restProps }) => (
  <S.TabItem {...restProps}>{children}</S.TabItem>
));
```
Al componente TabItem le agregaremos el componente con estilo **<S.TabItem/>**. 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.
```jsx
//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;
```
Ahora podemos utilizar el componente de la siguiente manera.

```jsx
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>
  );
}
```

El resultado final quedaría de la siguiente manera.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/bd9isopqiihi8ju4sg4d.PNG)

Aquí puedes [ver el código y un demo](https://codesandbox.io/s/react-tabs-with-styled-components-woih2)