Redux - Core Concepts
Redux - Core Concepts
Section titled “Redux - Core Concepts”Redux revolves around a few fundamental concepts that ensure predictable state management in JavaScript applications. Understanding these is key to using Redux effectively.
1. Single Source of Truth (Store)
Section titled “1. Single Source of Truth (Store)”The entire state of your application is stored in a single JavaScript object within a single Store. This makes it easier to track changes, debug issues, and implement features like state persistence or server-side rendering. Instead of having state scattered across various components, you have one centralized place.
Example State Structure:
{ // State managed by itemsSlice items: { list: [], status: 'idle', error: null }, // State managed by authSlice auth: { user: null, token: null }, // Other parts of the state...}2. State is Read-Only (Actions)
Section titled “2. State is Read-Only (Actions)”The only way to change the state is by dispatching an Action. Actions are plain JavaScript objects that describe what happened. They must have a type property (usually a string constant) indicating the type of action being performed. They can optionally carry a payload containing data necessary for the state change.
While you can create actions manually, Redux Toolkit’s createSlice generates action creators automatically. These are functions that return the action object.
// Example action object (conceptually){ type: 'counter/increment', // Format often used by createSlice: 'sliceName/reducerName'}
// Another example with payload{ type: 'items/itemAdded', payload: { id: 1, name: 'New Item' } // Data associated with the action}
// Using an action creator generated by createSliceimport { increment } from './features/counter/counterSlice';dispatch(increment()); // Dispatches { type: 'counter/increment' }
import { itemAdded } from './features/items/itemsSlice';dispatch(itemAdded({ id: 1, name: 'New Item' })); // Dispatches { type: 'items/itemAdded', payload: { ... } }Dispatching an action doesn’t directly change the state; it merely signals an intent to change it.
3. Changes are Made with Pure Functions (Reducers)
Section titled “3. Changes are Made with Pure Functions (Reducers)”To specify how the state tree is transformed by actions, you write pure Reducers. A reducer is a function that accepts the previous state and an action, and returns the next state. (previousState, action) => newState.
Reducers must be pure: they should not mutate the original state, perform side effects (like API calls), or rely on external variables. Given the same inputs, they must always produce the same output.
Redux Toolkit’s createSlice helps define reducers in a more organized way using an object where keys correspond to action types (implicitly) and values are the reducer functions for those actions. It uses Immer internally to simplify writing immutable update logic.
// Inside createSlice's 'reducers' option:
reducers: { increment: (state) => { // Immer handles immutability: this looks like mutation, // but produces a new state object. state.value += 1; }, itemAdded: (state, action) => { // action.payload contains the data for the new item state.list.push(action.payload); }}Data Flow
Section titled “Data Flow”The typical data flow in a Redux application (especially with React) is unidirectional:
- UI Event: Something happens in the UI (e.g., button click).
- Dispatch Action: An action creator is called, and the resulting action is dispatched using
dispatch(action). This is often done via theuseDispatchhook from React Redux. - Reducer Execution: The Redux store runs the root reducer function with the current state and the dispatched action. The appropriate slice reducer(s) process the action.
- State Update: The reducer returns the new state object. The store saves this new state.
- UI Update: The store notifies the UI (e.g., React components connected via
useSelector) that the state has changed. Components reading the affected parts of the state re-render with the new data.
This predictable, unidirectional flow makes applications easier to understand, debug, and maintain.