Full-Stack Architecture Patterns

Master architecture patterns for building scalable full-stack applications.

intermediate Backend Development 6 hours

Chapter 6: State Management

Chapter 6 of 15

Chapter 6: State Management

6.1 Client-Side State

Client-side state management handles data in the browser. Different approaches suit different application needs.

Local Component State:

  • State within individual components
  • Simple for small applications
  • React useState, Vue data()
  • Not shared across components
// React useState
const [count, setCount] = useState(0);
const [user, setUser] = useState(null);

// Update state
setCount(count + 1);
setUser({ name: 'John', email: 'john@example.com' });

Global State Management:

  • Shared state across components
  • Redux, Zustand, Context API
  • Centralized state store
  • Predictable state updates
// Redux example
const store = createStore(reducer);

// Action
const increment = () => ({ type: 'INCREMENT' });

// Reducer
const reducer = (state = { count: 0 }, action) => {
    switch (action.type) {
        case 'INCREMENT':
            return { count: state.count + 1 };
        default:
            return state;
    }
};

// Use in component
const count = useSelector(state => state.count);
dispatch(increment());

State Management Libraries:

  • Redux: Predictable state container
  • Zustand: Lightweight state management
  • MobX: Observable state
  • Context API: Built into React
  • Vuex: Vue.js state management

6.2 Server-Side State

Server-side state represents data stored on the server. Applications need strategies to synchronize client and server state.

Server State Characteristics:

  • Persisted in database
  • Shared across users
  • Requires API calls to access
  • Can become stale

State Synchronization:

  • Fetch data from server on component mount
  • Update local state when server data changes
  • Handle loading and error states
  • Implement caching strategies
// Fetch server state
useEffect(() => {
    fetch('/api/users')
        .then(res => res.json())
        .then(data => setUsers(data))
        .catch(err => setError(err));
}, []);

// Update server state
const updateUser = async (id, data) => {
    const response = await fetch(`/api/users/${id}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data)
    });
    const updated = await response.json();
    setUsers(users.map(u => u.id === id ? updated : u));
};

6.3 State Management Patterns

Common patterns help manage state effectively.

Flux Pattern:

  • Unidirectional data flow
  • Actions trigger state changes
  • Predictable updates
  • Used by Redux

Observer Pattern:

  • Components subscribe to state changes
  • Automatic updates when state changes
  • Used by MobX, Vue reactivity

State Normalization:

  • Store data in flat structure
  • Avoid nested objects
  • Use IDs for relationships
  • Easier to update and query

6.4 State Management Best Practices

Follow best practices for effective state management.

  • Keep state as local as possible
  • Lift state up only when necessary
  • Normalize nested state
  • Use immutable updates
  • Separate server and client state
  • Implement proper error handling
  • Cache server state appropriately