Component Pattern

Controlled / Uncontrolled Component

Controlled Component

  • Controlled components are form elements (like input, textarea, or select) that are managed by React state.

import React, { useState } from 'react';

function ControlledComponent() {
  const [value, setValue] = useState('');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    alert('A name was submitted: ' + value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" value={value} onChange={handleChange} />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
}

export default ControlledComponent;

Uncontrolled Component

  • Uncontrolled components in React manage their own state internally rather than relying on React state.

  • Using the ref attribute to create a reference (this.inputRef) to the DOM node of the input field to access the internal state

import React, { useRef } from 'react';

function UncontrolledComponent() {
  const inputRef = useRef(null); // Create a ref to hold the input DOM element

  const handleSubmit = () => {
    // Access the input value using the ref
    console.log(inputRef.current.value);
  };

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleSubmit}>Submit</button>
    </div>
  );
}

export default UncontrolledComponent;

Pro & Con

Controlled Component

  • Using controlled components ensures that the form data is always in sync with the React state. This predictability comes from having a single source of truth for the data and easier to debug

  • Easier to integrate with form library, e.g: React Hook Form

Uncontrolled Component

  • Applicable for the form that is straightforward with minimal input fields and does not require complex validation or dynamic updates based on other form inputs.

  • Validation logic often involves accessing and checking each input's value directly through refs (ref.current.value). This approach can lead to more manual and error-prone validation code, especially in forms with complex validation requirements.

Composable Component

import { createFileRoute } from "@tanstack/react-router";
import MyTable from "../../components/myTable";

export const Route = createFileRoute("/_authenticated/my-table")({
  component: () => <MyTablePage />,
});

const MyTablePage = () => {
  return (
    <MyTable>
      <MyTable.Search />
      <MyTable.Table />
      <MyTable.Pagination />
    </MyTable>
  );
};
import { ColumnDef, createColumnHelper } from "@tanstack/react-table";
import React from "react";
import { Product } from "../../types/my-table";
import Checkbox from "./checkBox";
import Row from "../layout/row";
import CustomTable from "./customTable";
import CustomSearch from "./customSearch";
import CustomPagination from "./customPagintaion";
import useMyTable from "../../hooks/my-table/useMyTable";
import TableContext from "../../contexts/tableProvider";

interface Props {
  children: React.ReactNode;
}
const MyTable = ({ children }: Props) => {
  const columnsHelper = createColumnHelper<Product>();
  const columns: ColumnDef<Product, any>[] = [];
  const { table } = useMyTable(columns);

  return (
    <TableContext.Provider value={{ table }}>
      <div className="relative overflow-x-auto shadow-md sm:rounded-lg py-10 px-5">
        {children}
      </div>
    </TableContext.Provider>
  );
};
MyTable.Search = CustomSearch;
MyTable.Table = CustomTable;
MyTable.Pagination = CustomPagination;
export default MyTable;
  • Easy to implement container & presentation pattern, state management & logic is on parent component (container layer) and pass the prop into child-component (presentation layer) through implementing context provider

  • Easier to understand the relationship between components and clear structure

  • Suitable to implement it with Form , Table

References

Last updated

Was this helpful?