React Refactoring (w/Storybook): Separate UI and Business Logic
Tags: react, webdev, typescript, javascript
Published: 2022-09-24 13:14:13

> React Refactoring (w/Storybook): Separate UI and Business Logic

When I joined my previous company, no one knew about the project code, the developer who made the project left.
There were many components, I didn't know what components had what kind of business logic. There were components that I wanted to reuse but I couldn't because they had a complicated business logic that could affect other components' behaviors. This makes it hard to recycle the component and reuse the UI, and sometimes I even made a component that already existed as similar looking.

One day, I decided to refactor them. For that, I separated UI and Business logic, and I used `storybook` for listing components. After this work, I was able to reuse components' UIs easily and see how they look.

---

I'll show you an example of this.
There is a component `TodayVisitorCard` that shows a number of visitors.

```typescript
import { useEffect, useState } from "react";
import styled from "@emotion/styled";

const CardWrapper = styled.div`
  border: 1px solid black;
  border-radius: 8px;
  box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
  padding: 24px;
  text-align: center;
  width: fit-content;
`;

interface VisitorResponse {
  total: number;
  today: number;
}

const TodayVisitorCard = () => {
  const [data, setData] = useState<VisitorResponse | null>();
  const [hasError, setHasError] = useState(false);

  const fetchVisitor = async () => {
    try {
      const res = await fetch("http://localhost:7777/visitor");

      if (res.status !== 200) {
        throw new Error(`Response status is ${res.status}.`);
      }

      const data = await res.json();
      setData(data);
    } catch (e) {
      console.error(e);
      setHasError(true);
      setData(null);
    }
  };

  useEffect(() => {
    fetchVisitor();
  }, []);

  if (data === undefined) return <div>loading...</div>;

  return (
    <CardWrapper>
      {hasError && <div>Something went wrong!</div>}
      {data && (
        <>
          <h2>Total: {data.total}</h2>
          <span>Today: {data.today}</span>
        </>
      )}
    </CardWrapper>
  );
};

export default TodayVisitorCard;
```

![Today Visitor Card](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dx2p84oqj3otytxpdixx.png)

Let's separate the logic.

```typescript
import { useEffect, useState } from "react";
import styled from "@emotion/styled";

const CardWrapper = styled.div`
  border: 1px solid black;
  border-radius: 8px;
  box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
  padding: 24px;
  text-align: center;
  width: fit-content;
`;

interface VisitorResponse {
  total: number;
  today: number;
}

const TodayVisitorCard = () => {
  const [data, setData] = useState<VisitorResponse | null>();
  const [hasError, setHasError] = useState(false);

  const fetchVisitor = async () => {
    try {
      const res = await fetch("http://localhost:7777/visitor");

      if (res.status !== 200) {
        throw new Error(`Response status is ${res.status}.`);
      }

      const data = await res.json();
      setData(data);
    } catch (e) {
      console.error(e);
      setHasError(true);
      setData(null);
    }
  };

  useEffect(() => {
    fetchVisitor();
  }, []);

  if (data === undefined) return <div>loading...</div>;

  return <TodayVisitorCardUI {...data} hasError={hasError} />;
};

const TodayVisitorCardUI = ({
  hasError,
  total,
  today,
}: {
  hasError?: boolean;
  total?: number;
  today?: number;
}) => {
  return (
    <CardWrapper>
      {hasError && <div>Something went wrong!</div>}
      {total && <h2>Total: {total}</h2>}
      {today && <span>Today: {today}</span>}
    </CardWrapper>
  );
};

export { TodayVisitorCard };
export default TodayVisitorCardUI;
```

Now, you can use other business logic easily with the UI, and
you can list up components UIs with `storybook` like below.


![file structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vznhpgro3uxblzqlshrm.png)

```typescript
import { ComponentStory, ComponentMeta } from "@storybook/react";

import TodayVisitorCard from ".";

export default {
  title: "components/TodayVisitorCard",
  component: TodayVisitorCard,
  args: {
    hasError: false,
  },
  argTypes: {
    today: {
      type: "number",
    },
    total: {
      type: "number",
    },
  },
} as ComponentMeta<typeof TodayVisitorCard>;

const Template: ComponentStory<typeof TodayVisitorCard> = (args) => (
  <TodayVisitorCard {...args} />
);

export const Example = Template.bind({});

Example.args = {
  today: 6522,
  total: 139,
};

export const Error = Template.bind({});

Error.args = {
  hasError: true,
};
```

![first storybook example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8rpcujmowzzv2hd4snny.png)


![second storybook example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h6guki7utwvtapxy49zw.png)


---

## Conclusion

In the real world, It would be more complicated. There might be lots of things you should consider about. Structures and Naming Conventions might be one of them. You should make your own strategy that fits your project.

I hope it would be helpful for someone.
Happy Coding!