State management is a core concept in frontend development, particularly for building large, complex single-page applications. Preact offers various approaches to manage component state, from simple local state to global state management solutions. This tutorial explores Preact’s state management strategies, including useState, useReducer, and third-party state management libraries.
Local State Management
Using useState
useState is the most commonly used Hook in Preact for managing simple state updates.
import { useState } from 'preact/hooks';
function Counter() {
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1);
}
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}Using useReducer
For more complex state logic, useReducer provides a structured approach, often used with a reducer function similar to Redux.
import { useReducer } from 'preact/hooks';
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}Global State Management
Using the Context API
Preact’s Context API enables passing state and callback functions without deep prop drilling.
import { createContext, useContext } from 'preact';
import { useState } from 'preact/hooks';
const CounterContext = createContext();
function CounterProvider({ children }) {
const [count, setCount] = useState(0);
return (
<CounterContext.Provider value={{ count, setCount }}>
{children}
</CounterContext.Provider>
);
}
function CounterDisplay() {
const { count } = useContext(CounterContext);
return <p>Count: {count}</p>;
}
function CounterControl() {
const { setCount } = useContext(CounterContext);
function increment() {
setCount(count => count + 1);
}
return <button onClick={increment}>Increment</button>;
}Using Third-Party Libraries
For more complex applications, consider third-party state management libraries like Redux, MobX, or Recoil.
- Redux Redux is a popular global state management library, and Preact supports integration with it.
import { Provider } from 'react-redux';
import { createStore } from 'redux';
const store = createStore(counterReducer);
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}- MobX MobX provides reactive data flows, ideal for complex state management.
import { observable, autorun } from 'mobx';
import { observer } from 'mobx-react-lite';
const store = observable({
count: 0,
increment() {
this.count++;
},
});
autorun(() => {
console.log(store.count);
});
const Counter = observer(() => {
return (
<div>
<p>Count: {store.count}</p>
<button onClick={() => store.increment()}>Increment</button>
</div>
);
});Performance Optimization
State management isn’t just about storing and updating state—it also involves performance considerations. Here are some optimization strategies:
- Using
useMemoanduseCallbackThese Hooks cache computation results and functions to prevent unnecessary re-renders. - Using
shouldComponentUpdateorReact.memoControl component re-rendering to update the view only when state actually changes.



