---
# System prepended metadata

title: React - How to design components ?
tags: [React]

---

React - How to design components ?
===
![](https://i.imgur.com/ZLirEbH.png)

###### tags: `React`

## Before 

Login and register form are the core components for pretty much every website, and I usually just code directly without destructing to smaller components.

The folder strucutre would be like this, I would create `LoginPage.tsx` and `SignupPage.tsx` and put everything inside this two components, I have to admit that this was not the best and efficient way and I was ended up repeating myself.

```jsonld
├── src
│   ├── components
│   │   ├── Application.tsx
│   ├── pages
        ├── LoginPage.tsx
        ├── SignupPage.tsx
```

```typescript
// LoginPage.tsx
export const LoginPage = () => {
  return (
    <form>
      <div>
        <label htmlFor="email">Email</label>
        <input 
           id="email"
           name="email"
           type="email"
           placeholder="Enter your email"
         />
      </div>
      <div>
        <label htmlFor="password">Password</label>
        <input 
           id="password"
           name="password"
           type="password"
           placeholder="Enter your password"
         />
      </div>
  )
}
```

## After trying to restructure components - 1

Look at the code above, first of all, I had to insert values one by one and did the same for `Register.tsx`, but it woild be easy for me to style because I could just create one css for styling forms.

But hey, I am using React, it supposed to be dynamic and be able to build by using smaller bricks, but what should I proceed when it comes to design components for my application?

Below was my initial plan, I literally break an entire form into four pieces.  

- `FormHeader.tsx`: Customizable header which can pass different values.
- `FormInput.tsx`: In order to write less code, we can deine values brforehand and pass as props into this child component,
- `ForRememberMe.tsx`: This is a simple component which contains its own state when user clicks the checkbox.
- `FormAction.tsx`: For all button actions, we can reuse this component.

![](https://i.imgur.com/i4pgyR3.png)

The folder structure would be like this.

```
|--src
    |-- components
    |   |-- authForm
    |       |-- authTypes.ts
    |       |-- FormHeader.tsx
    |       |-- FormInput.tsx
    |       |-- FormRememberMe.tsx
    |       |-- FormAction.tsx
    |-- pages
        |-- LoginPage.tsx
        |-- SignupPage.tsx
```

First thing first, let's define the values that will be passing as props.

```typescript
// authTypes.ts

export interface IAuthForm {
  labelText: string;
  labelFor: string;
  id: string;
  name: string;
  type: string;
  autoComplete: string;
  isRequired: boolean;
  placeHolder: string;
}

export interface IHEADER {
  heading: string;
  paragraph: string;
  linkTitle: string;
  linkUrl: string;
}

export interface IFormAction {
  handleSubmit: () => void;
  type: string;
  text: string;
  name: string;
}

export const LOGIN: IAuthForm[] = [
  {
    labelText: 'Email',
    labelFor: 'email-address',
    id: 'email-address',
    name: 'email',
    type: 'email',
    autoComplete: 'email',
    isRequired: true,
    placeHolder: 'Email Address',
  },
  {
    labelText: 'Password',
    labelFor: 'password',
    id: 'password',
    name: 'password',
    type: 'password',
    autoComplete: 'password',
    isRequired: true,
    placeHolder: 'Password',
  },
];

export const SIGNUP: IAuthForm[] = [
  {
    labelText: 'User Name',
    labelFor: 'user-name',
    id: 'user-name',
    name: 'user-name',
    type: 'text',
    autoComplete: 'user',
    isRequired: true,
    placeHolder: 'User Name',
  },
  {
    labelText: 'Email',
    labelFor: 'email-address',
    id: 'email-address',
    name: 'email',
    type: 'email',
    autoComplete: 'email',
    isRequired: true,
    placeHolder: 'Email Address',
  },
  {
    labelText: 'Password',
    labelFor: 'password',
    id: 'password',
    name: 'password',
    type: 'password',
    autoComplete: 'password',
    isRequired: true,
    placeHolder: 'Password',
  },
];

export const LOGINHEADER: IHEADER = {
  heading: 'Login your account',
  paragraph: "Don't have an account yet ? ",
  linkTitle: 'Signup here',
  linkUrl: '/signup',
};

export const SIGNUPHEADER: IHEADER = {
  heading: 'Sign up Here',
  paragraph: 'Already a member ? ',
  linkTitle: 'Login here',
  linkUrl: '/login',
};

```

Inside `SignupPage.tsx`, I had to import my predefine values and passed as props. For `input`, since it's an array that contained 3 objects, I could map through them.

```typescript=
// SignupPage.tsx
export const SignupPage = () => {
  const id = useId();

  const handleSubmit = (e: React.FormEvent<HTMLInputElement>): void => {
    e.preventDefault();
    console.log('hello');
  };
  return (
    <FormWrapper>
      <div>
        <FormHeader
          heading={SIGNUPHEADER.heading}
          paragraph={SIGNUPHEADER.paragraph}
          linkTitle={SIGNUPHEADER.linkTitle}
          linkUrl={SIGNUPHEADER.linkUrl}
        />
        <form>
          {SIGNUP.map((item) => (
            <FormInput
              key={id}
              labelText={item.labelText}
              labelFor={item.labelFor}
              id={item.id}
              name={item.name}
              type={item.type}
              autoComplete={item.autoComplete}
              isRequired={item.isRequired}
              placeHolder={item.placeHolder}
            />
          ))}
        </form>
        <FormRememberMe />
        <FormAction
          handleSubmit={() => handleSubmit}
          type="button"
          text="Sign up"
          name="Sign up"
        />
      </div>
    </FormWrapper>
  );
};

```

```typescript
// LoginPage.tsx

export const SignupPage = () => {
  const id = useId();

  const handleSubmit = (e: React.FormEvent<HTMLInputElement>): void => {
    e.preventDefault();
    console.log('hello');
  };
  return (
    <FormWrapper>
      <div>
        <FormHeader
          heading={SIGNUPHEADER.heading}
          paragraph={SIGNUPHEADER.paragraph}
          linkTitle={SIGNUPHEADER.linkTitle}
          linkUrl={SIGNUPHEADER.linkUrl}
        />
        <form>
          {SIGNUP.map((item) => (
            <FormInput
              key={id}
              labelText={item.labelText}
              labelFor={item.labelFor}
              id={item.id}
              name={item.name}
              type={item.type}
              autoComplete={item.autoComplete}
              isRequired={item.isRequired}
              placeHolder={item.placeHolder}
            />
          ))}
        </form>
        <FormRememberMe />
        <FormAction
          handleSubmit={() => handleSubmit}
          type="button"
          text="Sign up"
          name="Sign up"
        />
      </div>
    </FormWrapper>
  );
};

```

```typescript
// FormHeader.tsx
export const FormHeader = ({
  heading,
  paragraph,
  linkTitle,
  linkUrl,
}: IHEADER) => {
  return (
    <div>
      <h2>{heading}</h2>
      <div>
        <p>{paragraph}</p>
        <span>
          <Link to={linkUrl}>{linkTitle}</Link>
        </span>
      </div>
    </div>
  );
};

```

```typescript
// FormInput.tsx

export const FormInput = ({
  labelText,
  labelFor,
  id,
  name,
  type,
  isRequired,
  placeHolder,
  autoComplete,
}: IAuthForm) => {
  return (
    <div>
      <label htmlFor={labelFor}>{labelText}</label>
      <input
        id={id}
        name={name}
        type={type}
        required={isRequired}
        placeholder={placeHolder}
        autoComplete={autoComplete}
      />
    </div>
  );
};
```

```typescript
// FormRememberMe.tsx

export const FormRememberMe = () => {
  return (
    <div>
      <div>
        <input type="checkbox" name="rememberMe" id="rememberMe" />
        <label htmlFor="rememberMe">Remember Me</label>
      </div>
      <div>
        <a href="#">Forgot Password?</a>
      </div>
    </div>
  );
};
```

This approach did help me to create resuable components, I can use some of them somewhere else, but I found out it's not event efficient when to implement styles, because I was using styled-components, I had to look up every file and styled it, eventhough I still could create one stylesheet and write down styles I need, but it would need me to jump one to another file,perhaps it's not maintainable and readable as well.

For this approach, I think it would be better to use CSS framework such like TailwindCSS or BootStrap, I can implement as inline style.

## After trying to restructure components - 2

Sice I wasn't fancy the first approach, I thought maybe I should really think which component has more chance to be reused, in this case, perhaps `FormInput.tsx` has a better chance, we could have another form for contact or some sort of survey, so I've decided to just keep `FormInput.tsx`.

In this case, the folder structure would be like this.

```
|--src
    |-- components
    |   |-- authForm
    |       |-- authTypes.ts
    |       |-- FormInput.tsx
    |       
    |-- pages
        |-- LoginPage.tsx
        |-- SignupPage.tsx
```

Here's the reframe one, I could implement styles only between `LoginPage.tsx` and `FormInput.tsx`.

```typescript
// LoginPage.tsx
export const LoginPage = () => {
  const handleSubmit = (e: React.FormEvent<HTMLInputElement>): void => {
    e.preventDefault();
    console.log('hello');
  };
  return (
    <FormWrapper>
      <FormInsideContainer>
        {/* Header */}
        <FormHeader>
          <h2>{LOGINHEADER.heading}</h2>
          <span>
            {LOGINHEADER.paragraph}
            <Link to={LOGINHEADER.linkUrl}>{LOGINHEADER.linkTitle}</Link>
          </span>
        </FormHeader>

        {/* form */}
        <InputContainer>
          {LOGIN.map((item) => (
            <FormInput
              labelText={item.labelText}
              labelFor={item.labelFor}
              id={item.id}
              name={item.name}
              type={item.type}
              autoComplete={item.autoComplete}
              isRequired={item.isRequired}
              placeHolder={item.placeHolder}
            />
          ))}
        </InputContainer>
        {/* Remember Me */}
        <RememberMeContainer>
          <div>
            <input type="checkbox" name="rememberMe" id="rememberMe" />
            <label htmlFor="rememberMe">Remember Me</label>
          </div>
          <div>
            <a href="#">Forgot Password?</a>
          </div>
        </RememberMeContainer>
        {/* Login Btn */}
        <ButtonContainer type="submit" name="signup">
          Login
        </ButtonContainer>
        <BackToHome>
          <Link to="/">
            <FiArrowLeft />
            Back to home page
          </Link>
        </BackToHome>
      </FormInsideContainer>
    </FormWrapper>
  );
};
```

## After trying to restructure components - 3

As a container component, it might be a bit overwhelming, and not readable due to syntaxes were too long.

I was thinking maybe I could amend again, this time, the folder structure would be just like approach 1.

```
|--src
    |-- components
    |   |-- authForm
    |       |-- authTypes.ts
    |       |-- FormHeader.tsx
    |       |-- FormInput.tsx
    |       |-- FormRememberMe.tsx
    |       |-- FormAction.tsx
    |-- pages
        |-- LoginPage.tsx
        |-- SignupPage.tsx
```

Code would shift like this.

```typescript
// LoginPage.tsx

export const LoginPage = () => {
  return (
    <FormWrapper>
      <FormInsideContainer>
        <FormHeader />
        <FormInput />
        <FormRememberMe />
        <FormAction
          btnText={BUTTONLOGIN.btnText}
          name={BUTTONLOGIN.name}
          title={BUTTONLOGIN.title}
        />
        <BackToHome>
          <Link to="/">
            <FiArrowLeft />
            Back to home page
          </Link>
        </BackToHome>
      </FormInsideContainer>
    </FormWrapper>
  );
};
```

Looked like it's cleaner than before, but since I made child components from uncontrolled to controlled, meaning they weren't flexible anymore.

> Reference:
> [React Dev-Controlled & uncontrolled components](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components)

Before this implementation, I didn't have to worry about the path, I imported all predefined values of `LOGIN` and `SINGUP` and passed them inside `FormInput.tsx` which was a simple component, it accept whatever values I passed, so when I tried to switch from login to signup, it would show the exact values.

With this implementation, since I made `FormInput.tsx` as a controlled component, that means I needed to add extra logic to identify the path and showed different values accordingly.

```typescript=
// FormInput.tsx

export const FormInput = () => {
  const location = useLocation();
  const path = location.pathname;
  return (
    <>
      {path === '/login' && (
        <InputContainer>
          {LOGIN.map((item) => (
            <div key={item.id}>
              <label htmlFor={item.labelFor}>{item.labelText}</label>
              <input
                required={item.isRequired}
                name={item.name}
                type={item.type}
                id={item.id}
              />
            </div>
          ))}
        </InputContainer>
      )}
      {path === '/signup' && (
        <InputContainer>
          {SIGNUP.map((item) => (
            <div key={item.id}>
              <label htmlFor={item.labelFor}>{item.labelText}</label>
              <input
                required={item.isRequired}
                name={item.name}
                type={item.type}
                id={item.id}
              />
            </div>
          ))}
        </InputContainer>
      )}
    </>
  );
};
```

## Conclusion
There're other solutions I probably did not think of, but I think approach 1 seems to be a better choice as I want to make it as readable and reusable as possible eventhough it might be troublesome to jump between files for styling.