Each component should focus on one responsilitiy. Components that handle states, calculations, or complex logic should not render JSX. To reduce the code complexity, a component can be split into stateful (container) or stateless (presentational) components

Stateful vs Stateless

  • Stateful (Container) Component : manage state, make calculations based on props/state, and handle complex logic
  • Stateless (Presentational) Component : render JSX

A simple React app will have one stateful component that holds the other stateless components

// JSX
// Main component (stateful)
import React, { useState } from 'react';
import Counter from './Counter';       // Stateless
import Display from './Display';       // Stateless

export default function App() {
  // Stateful part: count
  const [count, setCount] = useState(0);

  // Function to update state
  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);

  return (
    <div style={{ textAlign: 'center', marginTop: '50px' }}>
      <h1>Stateful vs Stateless Example</h1>
      
      {/* Stateless component, receives props */}
      <Display count={count} />
      <Counter onIncrement={increment} onDecrement={decrement} />
    </div>
  );
}
// JSX
// Counter.js - render the buttons (stateless)
import React from 'react';

export default function Counter({ onIncrement, onDecrement }) {
  return (
    <div>
      <button onClick={onDecrement}>-</button>
      <button onClick={onIncrement}>+</button>
    </div>
  );
}
// JSX
// Display.js - render the header (stateless)
import React from 'react';

export default function Display({ count }) {
  return <h2>Current Count: {count}</h2>;
}

In this sample, the whole App component and its children will re-render (update the Virtual DOM) when the buttons are clicked because App's state got changed. Using memo hook can helps avoid unnecessary Virtual DOM recalculations for performance optimization.


Comparing with Next.js Server/Client Components

Server/Client component notes: link

  • Server Component (Next.js) :
    • Fetch data from backend and handle API
    • Authorization, tokens, and any secrets that shouldn't be run on client
    • Perform heavy process client can't do
  • Client Component (Next.js) :
    • Manages states, hooks
    • Use browser APIs
    • Interactive and dynamic UI
  • Stateful Component :
    • Manages states, hooks, event handlers
    • Perform complex logic
  • Stateless Component :
    • Render JSX

Separating code to make components mobile-ready for React Native: link