Predictable state container for JavaScript apps
—
Action creators and binding utilities for managing action dispatch. Actions are the only way to send data to the store, and action creators provide a clean way to create and dispatch actions with automatic binding to the store's dispatch function.
Wraps action creators with dispatch calls so they can be invoked directly without manually calling dispatch.
/**
* Wraps a single action creator with dispatch
* @param actionCreator - A function that creates actions
* @param dispatch - The dispatch function from your Redux store
* @returns The action creator wrapped with dispatch call
*/
function bindActionCreators<A, C extends ActionCreator<A>>(
actionCreator: C,
dispatch: Dispatch
): C;
/**
* Wraps multiple action creators with dispatch
* @param actionCreators - Object whose values are action creator functions
* @param dispatch - The dispatch function from your Redux store
* @returns Object with same keys, but action creators wrapped with dispatch
*/
function bindActionCreators<A, M extends ActionCreatorsMapObject<A>>(
actionCreators: M,
dispatch: Dispatch
): M;
/**
* Wraps a single action creator with flexible typing
* @param actionCreator - A function that creates actions
* @param dispatch - The dispatch function from your Redux store
* @returns The action creator wrapped with dispatch call
*/
function bindActionCreators<A extends ActionCreator<any>, B extends ActionCreator<any>>(
actionCreator: A,
dispatch: Dispatch
): B;
function bindActionCreators<M extends ActionCreatorsMapObject, N extends ActionCreatorsMapObject>(
actionCreators: M,
dispatch: Dispatch
): N;Usage Examples:
import { bindActionCreators } from "redux";
// Action creators
const increment = () => ({ type: "INCREMENT" });
const decrement = () => ({ type: "DECREMENT" });
const addTodo = (text: string) => ({ type: "ADD_TODO", payload: { text } });
// Bind single action creator
const boundIncrement = bindActionCreators(increment, store.dispatch);
boundIncrement(); // Automatically dispatches the action
// Bind multiple action creators
const actionCreators = { increment, decrement, addTodo };
const boundActionCreators = bindActionCreators(actionCreators, store.dispatch);
// Use bound action creators directly
boundActionCreators.increment(); // Dispatches increment action
boundActionCreators.addTodo("Learn Redux"); // Dispatches add todo action
// Common pattern in React components
const mapDispatchToProps = (dispatch) => bindActionCreators({
increment,
decrement,
addTodo
}, dispatch);The interface that action creator functions must implement.
/**
* An action creator is a function that creates an action
* @template A - The type of action returned
* @template P - The parameter types accepted by the action creator
*/
interface ActionCreator<A, P extends any[] = any[]> {
(...args: P): A;
}
/**
* Object whose values are action creator functions
* @template A - The type of actions created
* @template P - The parameter types accepted by action creators
*/
interface ActionCreatorsMapObject<A = any, P extends any[] = any[]> {
[key: string]: ActionCreator<A, P>;
}Usage Examples:
// Simple action creators
const reset: ActionCreator<{ type: "RESET" }> = () => ({ type: "RESET" });
const setCount: ActionCreator<{ type: "SET_COUNT"; payload: number }, [number]> = (
count: number
) => ({ type: "SET_COUNT", payload: count });
// Action creators with multiple parameters
const updateUser: ActionCreator<
{ type: "UPDATE_USER"; payload: { id: string; name: string } },
[string, string]
> = (id: string, name: string) => ({
type: "UPDATE_USER",
payload: { id, name }
});
// Action creator map
const userActionCreators: ActionCreatorsMapObject = {
login: (username: string, password: string) => ({
type: "LOGIN",
payload: { username, password }
}),
logout: () => ({ type: "LOGOUT" }),
updateProfile: (profile: object) => ({
type: "UPDATE_PROFILE",
payload: profile
})
};The fundamental action types that all Redux actions must extend.
/**
* An action is a plain object that represents an intention to change the state
* @template T - The type of the action's type tag
*/
type Action<T extends string = string> = {
type: T;
};
/**
* An Action type which accepts any other properties
* This is mainly for the use of the Reducer type
*/
interface UnknownAction extends Action {
[extraProps: string]: unknown;
}
/**
* An Action type which accepts any other properties
* @deprecated Use Action or UnknownAction instead
*/
interface AnyAction extends Action {
[extraProps: string]: any;
}Usage Examples:
// Basic action
const basicAction: Action = { type: "BASIC_ACTION" };
// Action with specific type
const specificAction: Action<"SPECIFIC_TYPE"> = { type: "SPECIFIC_TYPE" };
// Action with payload using UnknownAction
const actionWithPayload: UnknownAction = {
type: "WITH_PAYLOAD",
payload: { data: "some data" },
meta: { timestamp: Date.now() }
};
// Typed action interface
interface CounterAction extends Action {
type: "INCREMENT" | "DECREMENT" | "SET_COUNT";
payload?: number;
}
const counterActions: CounterAction[] = [
{ type: "INCREMENT" },
{ type: "DECREMENT" },
{ type: "SET_COUNT", payload: 10 }
];Action creators that return functions (thunks) for async operations:
// Async action creator (requires redux-thunk middleware)
const fetchUser = (userId: string) => {
return async (dispatch: Dispatch, getState: () => any) => {
dispatch({ type: "FETCH_USER_START" });
try {
const user = await api.getUser(userId);
dispatch({ type: "FETCH_USER_SUCCESS", payload: user });
} catch (error) {
dispatch({ type: "FETCH_USER_ERROR", payload: error.message });
}
};
};
// Bind async action creators
const boundAsyncActions = bindActionCreators({
fetchUser,
fetchPosts: (userId: string) => async (dispatch: Dispatch) => {
// async logic
}
}, store.dispatch);Functions that create action creators:
// Action creator factory
const createAsyncActionCreators = (entityName: string) => ({
request: () => ({ type: `${entityName.toUpperCase()}_REQUEST` }),
success: (data: any) => ({
type: `${entityName.toUpperCase()}_SUCCESS`,
payload: data
}),
failure: (error: string) => ({
type: `${entityName.toUpperCase()}_FAILURE`,
payload: error
})
});
// Create action creators for different entities
const userActions = createAsyncActionCreators("user");
const postActions = createAsyncActionCreators("post");
// Bind them to dispatch
const boundUserActions = bindActionCreators(userActions, store.dispatch);Selectively bind action creators based on conditions:
const createBoundActions = (user: User, dispatch: Dispatch) => {
const baseActions = { logout, updateProfile };
if (user.role === "admin") {
return bindActionCreators({
...baseActions,
deleteUser,
banUser,
promoteUser
}, dispatch);
}
return bindActionCreators(baseActions, dispatch);
};Creating fully type-safe action creators with TypeScript:
// Define action types
type UserActionTypes =
| { type: "SET_USER"; payload: User }
| { type: "CLEAR_USER" }
| { type: "UPDATE_USER_FIELD"; payload: { field: keyof User; value: any } };
// Type-safe action creators
const userActionCreators = {
setUser: (user: User): UserActionTypes => ({
type: "SET_USER",
payload: user
}),
clearUser: (): UserActionTypes => ({
type: "CLEAR_USER"
}),
updateUserField: <K extends keyof User>(
field: K,
value: User[K]
): UserActionTypes => ({
type: "UPDATE_USER_FIELD",
payload: { field, value }
})
};
// Bind with full type safety
const boundUserActions = bindActionCreators(userActionCreators, store.dispatch);Install with Tessl CLI
npx tessl i tessl/npm-redux