re-action provides a structured way to work with useReducer by offering a collection of hooks, utilities, and component creators. It streamlines action binding, context management, and reducer setup, making state management more intuitive and scalable.
In modern React development, managing complex state has become increasingly challenging. While the useReducer Hook offers a way to handle state logic, developers often encounter several pain points:
- Verbose Syntax: Manually dispatching actions can lead to boilerplate code, making components harder to read and maintain.
- Scalability Issues: As applications grow, setting up and managing reducers and contexts can become cumbersome, hindering scalability.
- TypeScript Integration: Ensuring type safety with useReducer requires additional effort, which can be error-prone and time-consuming.
Recognizing these challenges, re-action was developed to streamline state management in React applications. By offering a suite of hooks, utilities, and component creators, it simplifies action binding, context management, and reducer setup. This not only reduces boilerplate but also enhances code readability and maintainability, allowing developers to focus on building features rather than managing state intricacies.
- Small Size: just 2KB minified
- Full TS support
- No more dispatch calls, just actions
- Simplified
useReducer
: Cleaner syntax for state management - Performance-optimized for efficiency
- Gradual Refactoring Support: Eases transitions in legacy code
npm install @type-hub/re-action
Action
type ACTION = { type: string; payload: any }
An object representing a state change request in useReducer. It consists of a type field (a string identifier) and an optional payload containing additional data.
ActionCreator
type ActionCreator<Payload> = (payload: Payload) => {
type: string
payload: Payload
}
A function that generates an Action object. It takes a payload as an argument and returns an object with a predefined type and the provided payload. This helps streamline action creation and reduces boilerplate.
ActionCreators
type ActionCreators = Record<string, ActionCreator<any>>
An object mapping action names (strings) to ActionCreator functions. This provides a structured way to define multiple action creators in a single place, making it easier to dispatch actions without manually constructing them.
- πͺ useBoundActions
- πͺuseBoundReducer
- ποΈ createActionContext
- ποΈ createReducerContext
- π createReducerStore
- π§ createActionCreators
- π GetActionTypes
const boundActions = useBoundActions(dispatch, actionsCreators)
useBoundActions is a React hook that binds action creators to a dispatch function, ensuring actions are dispatched directly when called. It memoizes the bound actions to maintain reference stability and prevent unnecessary re-renders. By dynamically mapping action creators to dispatch, it simplifies state management in a Redux-like setup.
const boundActions = useBoundReducer(reducer, actionsCreators, initState)
useBoundReducer is a React hook that combines useReducer with useBoundActions, providing a stateful reducer along with action creators that are automatically bound to dispatch. It ensures that actions are dispatched seamlessly when called, simplifying state updates while maintaining reference stability. The hook returns the current state, the bound action creators, and the raw dispatch function for flexibility.
const { TestProvider, useTest, useTestBoundActions } = createActionContext(
actionsCreators,
"Name",
)
setupActions is a utility that creates a context-based action bincreateActionContextn the application. The returned object includes a React context for actions and a dynamically named hook (use[DisplayName]BindedActions) for binding actions to a given dispatch function.
const {
TestActionsProvider,
TestStateProvider,
useTestBoundActions,
useTestReducer,
useTestState,
} = createReducerContext(reducer, actionCreators, "DisplayName)
createReducerContext is a utility that generates a named reducer context, along with state and action contexts, enabling structured state management. It creates a dynamically named hook (use[DisplayName]Reducer) that initializes a reducer with bound actions while also exposing separate contexts for state and actions. This ensures encapsulated, context-aware state management within a React application.
const { TestProvider, useTestState, useTestBoundActions } = create(
reducer,
actionCreators,
"DisplayName,
)
create is a utility that sets up a fully encapsulated state management system using React context and a reducer. It dynamically generates a provider component ([DisplayName]Provider) to manage state and actions, along with hooks (use[DisplayName]State and use[DisplayName]Actions) for accessing them. This allows for a structured and reusable approach to managing state across a React application.
export const actionCreators = createActionCreators({
increment: (amount: number) => amount,
decrement: (amount: number) => amount,
})
declare const testAction: any
if (actionCreators.increment.match(testAction)) {
testAction // { type: "increment"; payload: number; }
}
This function generates action creators from a given lookup object containing functions. It iterates over the keys of the provided function lookup, wrapping each function to create a standardized action object containing a type derived from the key and a payload produced by invoking the corresponding function. This approach ensures type safety by inferring input parameters and return types, making it easier to define consistent and predictable action creators for state management in a functional and type-driven way.
Each generated actionCreator includes a .match(action) method, which checks whether the given action matches the type of actions created by that action creator.
const actionCreators = createActionCreators(["a", "b", "c"])
actionCreators.a.match({ type: "a" }) // true
actionCreators.b.match({ type: "b" }) // true
actionCreators.c.match({ type: "c" }) // true
Each generated actionCreator includes a type field, which contains the type of the action created by that action creator.
const actionCreators = createActionCreators(["a", "b", "c"])
actionCreators.a.type // "a"
actionCreators.b.type // "b"
actionCreators.c.type // "c"
const contextName = "Test"
const { TestProvider, useTest } = contextFactory<State, typeof contextName>(
contextName,
)
This utility function creates a strongly-typed React context factory, generating a custom provider and a corresponding hook for accessing the context value. It ensures that consumers of the hook cannot access the context outside of its provider, throwing an error if misused. The provider is wrapped in React.memo to optimize re-renders, and both the provider and hook names are dynamically derived from an optional display name. This approach simplifies context creation by enforcing type safety and reducing boilerplate when managing shared state in a React application.
export const actionCreators = createActionCreators({
increment: (amount: number) => amount,
decrement: (amount: number) => amount,
})
type Actions = GetActions<typeof actionCreators>
// | { type: "increment"; payload: number; }
// | { type: "decrement"; payload: number; }
const reducer = (state: State, action: Actions): State => {
/* ... */
return state
}
GetActions extracts the union of all possible action objects returned by an action creators object. It iterates over each key in AC, retrieves the return type of the corresponding function, and combines them into a single union type.
export const actionCreators = createActionCreators({
increment: (amount: number) => amount,
decrement: (amount: number) => amount,
})
type ActionTypes = GetActionTypes<typeof actionCreators>
// "increment" | "decrement"
GetActionTypes extracts the union of all possible action types returned by an action creators object. It iterates over each key in AC, retrieves the return type of the corresponding function, and combines them into a single union type.