Action Types and Action Creators in React-Redux

Define the two task operations as constants, write action creators that return typed action objects, and use the actions/index.js barrel pattern to simplify imports across components.

June 27, 20263 min read3 / 5

The task manager UI is ready. The Collapsible toggle works. The initial tasks render. What does not exist yet: any way to actually create or delete a task. That is what this post adds.

Two actions exist in this app. That means two constants, two action creators, and one barrel file.

Action type constant flows through the action creator to dispatch and into the reducer ExpandAction type constant flows through the action creator to dispatch and into the reducer

The Action Types File

Create src/constants/action-types.js:

JavaScript
export const CREATE_TASK = 'CREATE_TASK'; export const DELETE_TASK = 'DELETE_TASK';

That is the entire file. Each operation the user can perform gets a named constant. The value equals the name.

Why constants matter here: If you write the string 'CREATE_TASK' directly in both the action creator and the reducer, a typo in either place produces a silent bug. The dispatch goes through, the reducer falls to the default case, state does not change, and nothing in the console tells you why. A named constant turns that into a ReferenceError caught immediately. We covered this in the pure Redux series. The same rule applies in every Redux project.

The Action Creator Functions

Create src/actions/tasks.js:

JavaScript
import * as actionTypes from '../constants/action-types'; export const createTask = (newTask) => ({ type: actionTypes.CREATE_TASK, payload: newTask, }); export const deleteTask = (taskId) => ({ type: actionTypes.DELETE_TASK, payload: taskId, });

createTask receives the full task object (id, title, date). deleteTask receives only the id. In both cases the function returns an action object. That object is what dispatch() will receive when the component calls it.

The import * as actionTypes pattern: When you have many action types, importing them one by one is verbose. import * as actionTypes pulls all named exports into one object so they are accessible as actionTypes.CREATE_TASK, actionTypes.DELETE_TASK, and so on. It scales cleanly as the constants file grows.

The Barrel Export

Create src/actions/index.js:

JavaScript
import { createTask, deleteTask } from './tasks'; export default { createTask, deleteTask };

This file re-exports everything from the actions/ folder as a single default export. Any component that needs to dispatch a task action imports from ../../../actions instead of pointing to tasks.js directly.

Why index.js? When you import a folder (not a file), the bundler automatically resolves to index.js inside that folder. import actions from '../../actions' picks up actions/index.js without needing to spell out the filename. As the app grows and you add employees.js, products.js, and other action files, each one gets imported into this central index.js. Components never need to know which file a specific action creator came from.

JavaScript
// components/tasks/Tasks.js (once Redux is connected) import actions from '../../actions'; // then in the component: dispatch(actions.createTask(newTaskObject)); dispatch(actions.deleteTask(taskId));

The action creators are ready. The next step is the reducer that handles them.

The Essentials

  1. Two action types (CREATE_TASK, DELETE_TASK) live as named string constants in constants/action-types.js. The value equals the name to avoid silent typo bugs.
  2. createTask(newTask) returns { type: CREATE_TASK, payload: newTask }. deleteTask(taskId) returns { type: DELETE_TASK, payload: taskId }. The payload carries exactly what the reducer needs.
  3. actions/index.js is a barrel file. It re-exports all action creators as a single default object. Components import the folder (actions), not individual files, keeping imports clean as the action set grows.

Further Reading and Watching