Extracting and Reusing Pre-existing Components using bit add

ni
nitsan7703 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
Copied

We'll have to modify package.json:

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

Installing Bit is as simple as running a single command:

npx @teambit/bvm install
Copied

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

bit init
Copied

Time to start tracking the component:

bit add src/input -i ui/input -s [your-bit.cloud-account].reuse-components

bit env set ui/input teambit.react/react
Copied

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
Copied

The component will be added to the .bitmap file:

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

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 and have a look at our beautiful independent component in the Workspace UI

bit install && bit compile && bit start
Copied

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" />;
Copied

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" />

'''
Copied

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');
});
Copied

Let's run the tests:

bit test
Copied

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.
Copied

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

bit tag -m "first version"
Copied

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:

bit login

bit export
Copied

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
Copied

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
Copied

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;
Copied

Let's run the dev server:

npm start
Copied

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 and import the component into it:

bit init

bit import nitsan770.reuse-components/ui/input
Copied

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 create react ui/button --scope [your-bit.cloud-account].reuse-components
Copied

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>
  );
}
Copied

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;
}
Copied

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>
  );
}
Copied

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%;
}
Copied

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"
Copied
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/[email protected]0.0.2
> nitsan770.reuse-components/ui/[email protected]0.0.1
Copied

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!