Extracting and Reusing Pre-existing Components using bit add

ni
nitsan7709 months ago

Finally, you completed the task of creating a fantastic input field for the Input in your app. You are happy with the outcome and will continue to work on your assignments.

Next week, a team leader from another team asks you for help: "I need this done as soon as possible. Would you be able to lend a hand?" You happily accept.

Your organization tries to maintain a consistent brand, so you're not surprised to find that they want you to build a similar input element as you built a week ago.

"I'll just copy and paste from my team's repository", you say to yourself. Copying and pasting is not difficult, but what if we want to add styles to the input? You'll have to change all occurrences of it. If there are only two components, it's fine, but what if there are hundreds?

In this blog post, you will learn how to reuse every component that you have created using Bit. This is not a mistake. The components you create can easily be reused across all of your organization's apps. Ready. Set. Let's go!

Put that input in there

Clearly, we want to reuse that input component, but we don't want to copy-paste since it is not scalable, so what can we do?

Using Bit, we can extract the input component. Bit will handle everything for us, from versioning, building, testing, and even managing our component dependencies.

Create a React project and place this folder in the src folder. This folder contains the implementation details for the input component.

npx create-react-app reuse-comps
CopiedCopy

We'll have to modify package.json:

"@testing-library/react": "12.1.5",
"react": "17.0.2",
"react-dom": "17.0.2",
CopiedCopy

Installing Bit is as simple as running a single command:

npx @teambit/bvm install
CopiedCopy

Now at the root of our project, we can initialize a Bit Workspace:

$bit
Copiedcopy

Time to start tracking the component:

$bit
Copiedcopy

We also have to modify the env of this component to use the React env:

$bit
Copiedcopy

Remember to replace [your-bit.cloud-account] with your bit.cloud account and open a remote scope called reuse-components 😉 .

You will see the following output:

tracking component ui/input:
added input.tsx
added index.ts
added input.module.scss
CopiedCopy

The component will be added to the .bitmap file:

"ui/input": {
  "scope": "",
  "version": "",
  "defaultScope": "nitsan770.reuse-components",
  "mainFile": "index.ts",
  "rootDir": "src/Input",
},
CopiedCopy

Congratulations, you have freed your component! With Bit tracking your component, you'll be able to use it in any React project you have. We'll talk about it soon.

Let's install all dependencies:

$bit
Copiedcopy

Compile all components to create the dist folder for our component:

$bit
Copiedcopy

And run the dev server so we can view our beautiful independent component in the Workspace UI:

$bit
Copiedcopy

The development server is up and running! However, we are unable to see anything 😔 . This is because we have not created any compositions for our newly created component.

Compositions allow components to be simulated in different variations. These can then be used for testing, visualization, and discoverability.

Let's create a composition for our component:

input.composition.tsx
import React from 'react';

import { Input } from './Input';

export const BasicInput = () => <Input buttonText="Submit" />;
CopiedCopy

Now we can see the component in the Workspace UI:

basic input

Yet it is difficult to understand how it works. Let's write some documentation.

Bit components' documentation is written in MDX. No more boring sentences; now you can embed anything you want to make your component more visually appealing.

input.docs.mdx
---
description: An input element with a built-in button.

labels: ["input", "submit form"]
---

import { Input } from "./input";

### Modify the text on the button

'''js live

<Input buttonText="Go" />

'''
CopiedCopy

It looks much better now, and consumers can try out the component before installing:

Last but not least, a good component is well-tested:

input.spec.tsx
import React from 'react';
import { screen, render } from '@testing-library/react';
import { BasicInput } from './input.composition';

it('Renders the input', () => {
  render(<BasicInput />);
  const input = screen.getByRole('textbox');
  expect(input).toBeInTheDocument();
});

it('Renders the button', () => {
  render(<BasicInput />);
  const button = screen.getByRole('button');
  expect(button).toBeInTheDocument();
});

it('Renders the button text', () => {
  render(<BasicInput />);
  const button = screen.getByRole('button');
  expect(button).toHaveTextContent('Submit');
});
CopiedCopy

Let's run the tests:

$bit
Copiedcopy

All tests have passed. 🎉

PASS src/input/input.spec.tsx
  ✓ Renders the input (67 ms)
  ✓ Renders the button (19 ms)
  ✓ Renders the button text (12 ms)



Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 5.549 s
Ran all test suites.
test has been completed in 13.718 seconds.
CopiedCopy

Our last step is to tag the component with a (first) version number.

bit tag -m "first version"
CopiedCopy

When a component is tagged, it will also go through a build process in addition to locking the current state of the source files.

In this process, the component will be compiled to a distributable format and packed into a tar file.

Once the component has been tagged, it's time to publish it.

First you must login to your bit.cloud account through the CLI:

$bit
Copiedcopy

Then you can publish the component:

$bit
Copiedcopy

The component will be exported to the remote scope on bit.cloud.

Now that it has been published, we can use it in any React project.

Let's try it out!

Open another React project:

npx create-react-app another-project
CopiedCopy

You can install the component using npm, yarn, or pnpm. Let's use pnpm:

choose pnpm
pnpm i @nitsan770/reuse-components.ui.input
## if you haven't yet, remember to add your bit.cloud account as a scoped registry:
# npm config set '@nitsan770:registry' https://node.bit.cloud
CopiedCopy

We can now use the component in the app.js file:

import logo from './logo.svg';
import { Input } from '@nitsan770/reuse-components.ui.input';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <Input buttonText="Submit" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;
CopiedCopy

Let's run the dev server:

npm start
CopiedCopy

It's a perfect match!

dev server running

Decomposing

The moment you started the dev server, a product manager from your company entered the room.

"What a nice button! I need one like that for my project!"

"Do you mean the input component?"

"No, I mean the button!"

Are you thinking about what I am thinking? Let's decompose the component into two smaller components!

Let's stay on the same project. Initialize a new Workspace:

$bit
Copiedcopy

And import the component into it:

$bit
Copiedcopy

The command above will import the input component into our Workspace. Once imported, we can then edit and export a new version. It's like installing it, but you are in control.

We will begin the refactor by creating a component called ui/button

$bit
Copiedcopy

Let's separate the button component from the input component:

button.tsx
import React, { ReactNode } from 'react';
import classNames from 'classnames';
import styles from './button.module.scss';

export type ButtonProps = {
  /**
   * a node to be rendered in the button
   */
  children?: ReactNode,
} & React.ButtonHTMLAttributes<HTMLButtonElement>;

export function Button({ children, className }: ButtonProps) {
  return (
    <button className={classNames(styles.button, className)} type="submit">
      {children}
    </button>
  );
}
CopiedCopy

Of course, it also has styles. Since some of the styles are specific to the input component implementation, we'll keep them in the input component:

button.module.scss
.button {
  font-size: 16px;
  line-height: 24px;
  align-items: center;
  background: #6c5ce7;
  border: none;
  border-radius: 8px;
  color: #fff;
  cursor: pointer;
  text-decoration: none;
  transition: background-color 0.3s ease-in-out;
  min-width: fit-content;
}
CopiedCopy

Finally, we can refactor the input. We are not changing the API because we don't want to break the component for those who already use it.

input.tsx
import React from 'react';
import { Button } from '@nitsan770/reuse-components.ui.button';
import styles from './input.module.scss';

export type InputProps = {
  buttonText: string,
} & React.InputHTMLAttributes<HTMLInputElement>;

export function Input({ buttonText, ...rest }: InputProps) {
  return (
    <div className={styles.inputContainer}>
      <input {...rest} className={styles.inputText}></input>
      <Button className={styles.button} children={buttonText} />
    </div>
  );
}
CopiedCopy

Here are the styles we are leaving in the input component:

input.module.scss
.button {
  position: absolute;
  width: 103px;
  height: 40px;
  right: 8px;
  top: 8px;
  width: 30%;
}
CopiedCopy

The last step is to tag the button component with its first version and the input component with its second version.

bit tag -m "decompose the button component from the input"
CopiedCopy
2 component(s) tagged
(use "bit export [collection]" to push these components to a remote")
(use "bit untag" to unstage versions)

changed components
(components that got a version bump)
> nitsan770.reuse-components/ui/input@0.0.2
> nitsan770.reuse-components/ui/button@0.0.1
CopiedCopy

Now we have two independent components that can be installed and used in any project.

Summary

We have learned how to separate the components from our projects into independent components that can be used anywhere.

Moreover, we learned how to decompose components into smaller pieces and use them in any project we like.

I hope you enjoyed this tutorial.

Please read our documentation if you want to learn more.

If you have any questions, please ask them in our slack channel.

Best of luck!