In this section, we will delve into the core concepts of Redux: Actions and Reducers. These are fundamental building blocks for managing state in a Redux application.
Key Concepts
Actions
- Definition: Actions are plain JavaScript objects that represent an intention to change the state.
- Structure: An action must have a
type
property, which is a string that indicates the type of action being performed. It can also have an optionalpayload
property that contains additional information needed to perform the action.
Reducers
- Definition: Reducers are pure functions that take the current state and an action as arguments and return a new state.
- Pure Functions: A pure function is a function that, given the same input, will always return the same output and does not have any side effects.
Actions in Detail
Creating Actions
Actions are created using action creators, which are functions that return an action object.
// Action Types const INCREMENT = 'INCREMENT'; const DECREMENT = 'DECREMENT'; // Action Creators const increment = () => { return { type: INCREMENT }; }; const decrement = () => { return { type: DECREMENT }; };
Dispatching Actions
Actions are dispatched to the store using the dispatch
method.
Reducers in Detail
Creating Reducers
Reducers handle the dispatched actions and update the state accordingly.
const initialState = { count: 0 }; const counterReducer = (state = initialState, action) => { switch (action.type) { case INCREMENT: return { ...state, count: state.count + 1 }; case DECREMENT: return { ...state, count: state.count - 1 }; default: return state; } };
Combining Reducers
In larger applications, you might have multiple reducers managing different parts of the state. These can be combined using combineReducers
.
import { combineReducers } from 'redux'; const rootReducer = combineReducers({ counter: counterReducer, // other reducers can be added here });
Practical Example
Setting Up Redux
-
Install Redux and React-Redux:
npm install redux react-redux
-
Create the Redux Store:
import { createStore } from 'redux'; import { Provider } from 'react-redux'; import rootReducer from './reducers'; // Assuming you have a rootReducer const store = createStore(rootReducer); const App = () => ( <Provider store={store}> <Counter /> </Provider> );
-
Connecting Components to Redux:
import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { increment, decrement } from './actions'; // Assuming you have these action creators const Counter = () => { const count = useSelector(state => state.counter.count); const dispatch = useDispatch(); return ( <div> <h1>{count}</h1> <button onClick={() => dispatch(increment())}>Increment</button> <button onClick={() => dispatch(decrement())}>Decrement</button> </div> ); }; export default Counter;
Exercises
Exercise 1: Create a New Action and Reducer
- Task: Create a new action type
RESET
that resets the count to zero. - Steps:
- Define the
RESET
action type. - Create an action creator for
RESET
. - Update the reducer to handle the
RESET
action.
- Define the
Solution:
// Action Types const RESET = 'RESET'; // Action Creators const reset = () => { return { type: RESET }; }; // Reducer const counterReducer = (state = initialState, action) => { switch (action.type) { case INCREMENT: return { ...state, count: state.count + 1 }; case DECREMENT: return { ...state, count: state.count - 1 }; case RESET: return { ...state, count: 0 }; default: return state; } };
Exercise 2: Combine Multiple Reducers
- Task: Create a new reducer to manage a list of items and combine it with the counter reducer.
- Steps:
- Create a new reducer for managing items.
- Combine the new reducer with the counter reducer using
combineReducers
.
Solution:
// Items Reducer const itemsReducer = (state = [], action) => { switch (action.type) { case 'ADD_ITEM': return [...state, action.payload]; case 'REMOVE_ITEM': return state.filter(item => item !== action.payload); default: return state; } }; // Combine Reducers const rootReducer = combineReducers({ counter: counterReducer, items: itemsReducer });
Common Mistakes and Tips
- Mutating State: Ensure that you do not mutate the state directly in reducers. Always return a new state object.
- Action Types: Use constants for action types to avoid typos and make the code more maintainable.
- Pure Functions: Reducers should be pure functions. Avoid side effects like API calls or modifying variables outside the function.
Conclusion
In this section, we covered the basics of Redux actions and reducers. We learned how to create and dispatch actions, how to write reducers to handle these actions, and how to combine multiple reducers. These concepts are crucial for managing state in a Redux application. In the next section, we will explore how to connect Redux to React components.
React Course
Module 1: Introduction to React
- What is React?
- Setting Up the Development Environment
- Hello World in React
- JSX: JavaScript Syntax Extension
Module 2: React Components
- Understanding Components
- Functional vs Class Components
- Props: Passing Data to Components
- State: Managing Component State
Module 3: Working with Events
Module 4: Advanced Component Concepts
- Lifting State Up
- Composition vs Inheritance
- React Lifecycle Methods
- Hooks: Introduction and Basic Usage
Module 5: React Hooks
Module 6: Routing in React
Module 7: State Management
- Introduction to State Management
- Context API
- Redux: Introduction and Setup
- Redux: Actions and Reducers
- Redux: Connecting to React
Module 8: Performance Optimization
- React Performance Optimization Techniques
- Memoization with React.memo
- useMemo and useCallback Hooks
- Code Splitting and Lazy Loading
Module 9: Testing in React
- Introduction to Testing
- Unit Testing with Jest
- Testing Components with React Testing Library
- End-to-End Testing with Cypress
Module 10: Advanced Topics
- Server-Side Rendering (SSR) with Next.js
- Static Site Generation (SSG) with Next.js
- TypeScript with React
- React Native: Building Mobile Apps