useReducerWithEffects
Reducer with side effects support. Perfect for finite state machines and multi-step workflows.
Live Demo
📋 Multi-Step Form
Step 1
Step 2
Step 3
Personal Information
📝 Event Log
No events yet
💡 How It Works:
- Reducer manages state transitions
- Effects handle side effects (API calls, timers)
- Perfect for finite state machines
- Multi-step workflows made easy
- Automatic cleanup on unmount
Code Examples
Basic Usage
import { useReducerWithEffects } from "@uiblock/hooks";
const [state, dispatch] = useReducerWithEffects({
reducer: (state, action) => {
// Handle state transitions
},
effects: {
SUBMIT: (state, action, dispatch) => {
// Side effect: API call
fetch('/api/submit', { method: 'POST', body: JSON.stringify(state) })
.then(() => dispatch({ type: 'SUCCESS' }))
.catch(() => dispatch({ type: 'ERROR' }));
}
}
}, initialState);Finite State Machine
import { createStateMachine, useReducerWithEffects } from "@uiblock/hooks";
const machine = createStateMachine({
initial: 'idle',
context: { retries: 0 },
states: {
idle: {
on: { FETCH: 'loading' }
},
loading: {
on: { SUCCESS: 'success', ERROR: 'error' },
effect: (context, event, send) => {
fetch('/api/data')
.then(() => send('SUCCESS'))
.catch(() => send('ERROR'));
}
},
success: {
on: { RESET: 'idle' }
},
error: {
on: { RETRY: 'loading', RESET: 'idle' }
}
}
});
const [state, dispatch] = useReducerWithEffects(machine, machine.initialState);How It Works
Here's the implementation:
import { useReducer, useEffect, useRef, Reducer } from 'react'
export type Action<T = string, P = any> = {
type: T
payload?: P
}
export type Effect<S, A> = (
state: S,
action: A,
dispatch: (action: A) => void
) => void | (() => void)
export function useReducerWithEffects<S, A extends Action>(
config: {
reducer: Reducer<S, A>
effects?: Record<string, Effect<S, A>>
},
initialState: S
): [S, (action: A) => void] {
const { reducer, effects = {} } = config
const [state, dispatch] = useReducer(reducer, initialState)
const effectCleanupRef = useRef<(() => void) | void>()
useEffect(() => {
return () => {
if (effectCleanupRef.current) {
effectCleanupRef.current()
}
}
}, [])
const dispatchWithEffects = (action: A) => {
// Cleanup previous effect
if (effectCleanupRef.current) {
effectCleanupRef.current()
effectCleanupRef.current = undefined
}
// Dispatch action
dispatch(action)
// Run effect if exists
const effect = effects[action.type]
if (effect) {
setTimeout(() => {
effectCleanupRef.current = effect(state, action, dispatchWithEffects)
}, 0)
}
}
return [state, dispatchWithEffects]
}💡 Use Cases
- Multi-step forms and wizards
- Finite state machines
- Complex workflows with side effects
- Authentication flows
- Checkout processes