Como criar componentes customizáveis e publicar no npm!
Tags: react, npm, opensource
Published: 2020-07-04 15:09:39

Nesse artigo, irei mostrar como criar componentes e como deixa-los customizáveis dependendo do que vier por props. E no final mostrarei como publicar no npm! (para instalar usando o comando npm install --save nome_da_lib) 😁

O meu componente vai ser um botão de loading, exemplo na imagem abaixo:

<p>&nbsp;</p>

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

<p>&nbsp;</p>

> Ignore os estilos, leve em conta que é representativo 😂

<p>&nbsp;</p>

Para iniciar sua lib, utilize o seguinte comando com o npx:

> npx create-react-app nome-da-sua-lib

<p>&nbsp;</p>

Sim, utilizaremos o comando de criar um projeto em React mesmo. No meu caso, como irei criar um button loading, o meu comando é:

> npx create-react-app button-loading

<p>&nbsp;</p>

Feito isso, você pode começar apagando todos os arquivos de dentro de `src` e deixando apenas o `index.js`.

Deixei o meu `index.js` assim:
```jsx
import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <div>
    <h1>Hello World</h1>
  </div>,
  document.getElementById('root')
);
```

<p>&nbsp;</p>

O nosso componente irá ficar na pasta `lib` dentro de `src`.

Minha pasta lib ficou assim:

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

<p>&nbsp;</p>

Dentro de `lib/index.js` eu [exporto](https://github.com/rafaelsevla/button-loading/blob/master/src/lib/index.js) meu botão para quando alguém for instalar a lib, importar com `import ButtonLoading from '@rafaelsevla/button-loading'` e não com `import ButtonLoading from '@rafaelsevla/button-loading/Button-Loading'`.

<p>&nbsp;</p>

Depois disso, criei um componente de botão sem estilo e importei na minha `index.js` de dentro de `src/`, para ir acompanhando a evolução do componente, e ficou assim:

`src/index.js`
```jsx
import React from 'react';
import ReactDOM from 'react-dom';

import ButtonLoading from './lib/ButtonLoading'

ReactDOM.render(
  <div>
    <ButtonLoading />
  </div>,
  document.getElementById('root')
);
```
<p>&nbsp;</p>

`Componente do botão:`
```jsx
import React from 'react';

const ButtonLoading = () => (
  <button>
    Click in me
  </button>
)

export default ButtonLoading;
```

<p>&nbsp;</p>

Agora eu adicionei alguns estilos no botão para ele ter as opções `primary` e `danger`. Como o foco do artigo não é estilização e sim como customizar componentes React, vou deixar o link dos estilos [aqui](https://github.com/rafaelsevla/button-loading/blob/master/src/lib/ButtonLoading.js#L30).

<p>&nbsp;</p>

Inclusive, utilizei styled components, caso tenha curiosidade para saber como funciona eu escrevi um artigo um tempo atrás sobre ele [aqui](https://dev.to/rafaelsevla/estilizando-com-styled-components-568g).

<p>&nbsp;</p>

Meu arquivo do botão ficou assim:

```jsx 
import React from 'react';

// importei o arquivo os componentes estilizados
import { Button, Spinner } from './styles.js'

// aqui defino meu dicionário de cores
const colors = {
  primary: '#7001e7',
  danger: '#9c0000'
}

// adicionei três propriedades que o botão pode receber e coloquei em seus devidos lugares
const ButtonLoading = ({ title, color, loading }) => (
  <Button color={colors[color]}>
    {/* aqui eu verifico se mostro o loading ou o texto do botão */}
    {loading
      ? <Spinner />
      : title}
  </Button>
);

// adicionei as propriedades padrão, caso o botão não receba nada
ButtonLoading.defaultProps = {
  title: 'Click in me',
  color: 'primary',
  loading: false
}

export default ButtonLoading;
```

<p>&nbsp;</p>

E para usar o botão, basta eu adicionar as props onde estiver meu botão.
Detalhe que no loading eu posso chamar um estado do meu componente (página) que é booleano.

```jsx
<ButtonLoading
  title='Meu Botão'
  color='danger'
  loading={meuEstadoBooleano}
/>
```

<p>&nbsp;</p>


Dessa forma, o componente já está sendo customizável. Ele já está recebendo props e reagindo da forma que se espera como por exemplo, o loading que inverte com o texto ou o `color` que pode ser `primary` ou `danger`.

<p>&nbsp;</p>

Caso queira colocar mais cores no botão, só precisaria adicionar mais uma cor `secondary` no dicionário `colors` e ela ser toda verde, por exemplo.

<p>&nbsp;</p>

Mas aí você pode se perguntar: como eu poderia deixar o botão quadrado ~ou redondo ou whatever~ em apenas uma tela mandando estilos por props ao invés de criar um tipo somente pra isso?

Basta colocar o botão para receber a prop styles e chamar no botão:

```jsx
const ButtonLoading = ({ title, color, loading, styles }) => (
  <Button color={colors[color]} styles={styles}>
    ...
  </Button>
);

ButtonLoading.defaultProps = {
  ...
  styles: {}
}
```

<p>&nbsp;</p>

E na chamada ficaria assim:

```react
<ButtonLoading styles={{ backgroundColor: '#f0f', borderRadius: 200, color: '#ff0' }} />
```

<p>&nbsp;</p>

Note que eu alterei a cor de fundo e do texto (isso é possível também, caso eu não me satisfaça com o que a lib já tem).

<center>
. . .
</center>

Uma propriedade que posso receber no meu botão são as cores do spinner, deixaria o branco e azul como default mas com opção de receber outras.

Como eu defino as cores do spinner dentro do styled component `Spinner`, eu abstraio essas duas cores para utilizar o que vem por props.

```jsx
export const Spinner = styled.div`
  @keyframes spinner {
    0% { transform: rotate(0deg); }
    50% { transform: rotate(1000deg); }
    100% { transform: rotate(360deg); }
  }

  border: 5px solid #f3f3f3; /* <-- branco */
  border-top: 5px solid #3498db; /* <-- azul */
  border-radius: 50%;
  width: 20px;
  height: 20px;
  animation: spinner 2s linear infinite;
`
```

<p>&nbsp;</p>

Eu passo a chamar o meu spinner assim:

```jsx
<Spinner
  spinnerBackground={spinnerBackground}
  spinnerColor={spinnerColor}
/>
```

<p>&nbsp;</p>

Antes de publicar no npm, podemos definir os tipos das props que nosso componente recebe instalando o `prop-types` com o comando `npm -i --save prop-types` e definindo os tipos no arquivo do componente:

```jsx
import t from 'prop-types'

...

ButtonLoading.propTypes = {
  title: t.string,
  color: t.oneOf(['primary', 'danger']),
  loading: t.bool,
  styles: t.object,
  spinnerBackground: t.string,
  spinnerColor: t.string
}
```

Você pode ler mais a respeito sobre ele [aqui](https://reactjs.org/docs/typechecking-with-proptypes.html). Acho que proptypes é algo para um artigo só dele... então não vou entrar muito em detalhes!

<p>&nbsp;</p>

E pronto, agora sabemos como definir propriedades de "múltipla escolha" (type que pode receber primary e danger), propriedades booleanas (o loading) e como deixar nosso componente customizável recebendo estilos ou cores por parâmetro e utilizando onde queremos.

<center>
. . .
</center>

Poderia ficar horas escrevendo formas de customizar o componente, mas acho que isso já é um grande norte pra quem está começando!

Agora chegou a hora de nós publicarmos no npm!

<center>
. . .
</center>

Primeiramente, precisamos configurar algumas coisas...

## O nome do pacote
O nome do pacote será o que usa para instalar `npm install --save nome_do_pacote`
Você pode defini-lo no `package.json` na propriedade `name`

## A versão

A minha lib terá a versão pelo padrão [Semantic Version](https://semver.org/), começando como 0.1.0

E caso eu adicione alguma cor, por exemplo, mudo o minor, ficando 0.2.0

Para publicar a lib é bastante simples: você só precisa executar o comando:

> npm publish --access=public

<p>&nbsp;</p>

Mas calma, antes de fazer isso você precisa ter um cadastro no NPM e configurar a sua máquina para poder publicar suas libs.

<p>&nbsp;</p>

O cadastro é simples: entre em [https://www.npmjs.com/](https://www.npmjs.com/) e cadastre-se!

<p>&nbsp;</p>

Você ainda pode se cadastrar via terminal com o comando `npm add user`.

Será pedido um nome de usuário, uma senha e um e-mail (que ficará público).

Se você já tem cadastro, só precisará fazer login com o comando `npm login`.

Após isso, você pode verificar se seus dados estão corretamente configurados com o comando `npm config ls`.

<p>&nbsp;</p>

Antes de publicar (fique tranquilo, já vamos chegar lá), precisamos configurar a build do nosso projeto.

Precisamos adicionar e configurar o babel para nos ajudar com a compilação do projeto.

Para adicionar utilize o comando `npm -i @babel/cli @babel/core --save-dev`

Crie o arquivo `.babelrc` na raíz do projeto com o seguinte script:

```json
{
  "presets": [["react-app", { "absoluteRuntime": false }]]
}
```

<p>&nbsp;</p>

Agora precisamos alterar o valor do script `build` no `package.json` para `"rm -rf dist && NODE_ENV=production babel src/lib --out-dir dist --copy-files --ignore __tests__,spec.js,test.js,__snapshots__"`

Com isso ele sempre irá apagar a pasta `dist`, mudando a variável de ambiente para `production` e gerando o build dos nossos arquivos dentro de `src/lib` com o babel e jogando para a pasta `dist`. Esse comando sempre apaga a pasta `dist` quando inicia para sempre gerar novos arquivos lá e não ocorrer conflitos durante o build.

<p>&nbsp;</p>

Ainda no `package.json`, remova a linha `"private": true` e adicione essas linhas para o npm saber quais arquivos devem ser enviados para o npm:

```json
"main": "dist/index.js",
"module": "dist/index.js",
"files": [
  "dist",
  "README.md"
],
"repository": {
  "type": "git",
  "url": "SEU_LINK_DO_REPOSITÓRIO"
},
```

<p>&nbsp;</p>

Feito isso, rode no seu terminal o comando `npm publish --access-public`.

Agora que você tem a lib no npm, qualquer alteração que você fizer só irá precisar alterar a versão no `package.json` e rodar apenas `npm publish`!

<p>&nbsp;</p>

Ah, e não esqueça de escrever como utilizar sua lib no README.md!

<center>
. . .
</center>

<p>&nbsp;</p>

Bom, agora que você tem uma lib publicada no npm é hora de me despedir! Espero que goste!

Se ficar alguma dúvida, sugestão ou crítica, não exite em me contatar!

Abraços e até a próxima \o