Exploring React-Redux, and Redux Toolkit: A Day of Learning

Exploring React-Redux, and Redux Toolkit: A Day of Learning

ยท

6 min read

Introduction:

Today marked a pivotal moment in my journey as a developer as I delved deep into the realms of React-Redux, and Redux Toolkit. In this blog post, I am thrilled to share the wealth of knowledge and discoveries I've gained about state management and the implementation of Redux store and slices in React applications.

Getting Started:

React is a JavaScript library for building user interfaces. React's component-based architecture and virtual DOM make it a popular choice for developing interactive web applications. As I worked through tutorials and documentation, I gained a deeper understanding of React's core concepts, such as components, props, state, and lifecycle methods.

Exploring State Management:

Before delving into the complex details or inner workings of Redux and Redux Toolkit, I first embarked on a journey to comprehend the vital role of state management in web development. Recognizing the importance of maintaining a centralized state accessible and modifiable across different components of an application laid the groundwork for my exploration of Redux and Redux Toolkit.

Creating Redux Store:

With a solid understanding of state management principles, I ventured into the creation of a Redux store, the heart of Redux-based applications. Below is the code snippet demonstrating the setup of a Redux store:

// store.js
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './reducers'; // Import your root reducer here

const store = configureStore({
  reducer: rootReducer, // Pass your root reducer to configureStore.
 //You can pass as many reducer as you want 
});

export default store;

In this snippet:

  • We import configureStore from @reduxjs/toolkit.

  • We use configureStore and pass our root reducer to it to create the Redux store.

  • Finally, we export the created store.

Provide the Redux Store to React:

Once we've created the store, we can provide access to it for our React components by encapsulating our application with a React-Redux <Provider> in src/index.js. Import the Redux store we've just created, wrap your <App> component with the <Provider>, and pass the store as a prop:

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import { store } from './app/store'
import { Provider } from 'react-redux'

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

Creating Redux Slice:

Next, I delved into the creation of Redux slices, which are modular units of Redux state and logic.

To create a slice, you need a string name to identify it, an initial state value, and one or more reducer functions to specify how the state can be updated. Once a slice is created, you can export the Redux action creators and the reducer function generated for the entire slice.

Redux mandates that all state updates must be performed immutably, meaning by making copies of data and updating those copies. However, Redux Toolkit's createSlice and createReducer APIs leverage Immer internally, enabling us to write update logic that appears to mutate state directly while ensuring that the updates are correctly translated into immutable updates behind the scenes.

Below is an example of how to create a Redux slice for managing a user entity:

// userSlice.js
import { createSlice } from '@reduxjs/toolkit';

// Initial state for the user slice
const initialState = {
  user: null,
  loading: false,
  error: null,
};

// Create a slice for user management
const userSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {
    setUser(state, action) {
      state.user = action.payload;
    },
    setLoading(state, action) {
      state.loading = action.payload;
    },
    setError(state, action) {
      state.error = action.payload;
    },
  },
});

// Export actions generated from the slice
export const { setUser, setLoading, setError } = userSlice.actions;

// Export the reducer
export default userSlice.reducer;

In this code:

  • We import createSlice from @reduxjs/toolkit.

  • We define the initial state for the user slice, which includes fields like user, loading, and error.

  • We use createSlice to define a slice named 'user', with the initial state and a set of reducer functions.

  • The reducer functions (setUser, setLoading, setError) are generated automatically based on the provided state fields. These functions will update the state immutably.

  • We export these generated actions (setUser, setLoading, setError) so that they can be dispatched to update the state.

  • Finally, we export the reducer function generated by createSlice, which will handle state updates based on dispatched actions.

Use Redux State and Actions in React Components:

We can now employ React-Redux hooks to enable React components to interact with the Redux store. We can retrieve data from the store using useSelector and dispatch actions using useDispatch.

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { setUser, setLoading, setError } from './userSlice'; // Import actions from userSlice
import axios from 'axios'; // Assume using Axios for HTTP requests

const App = () => {
  const user = useSelector(state => state.user.user); // Select the user state from Redux store
  const loading = useSelector(state => state.user.loading); // Select the loading state from Redux store
  const error = useSelector(state => state.user.error); // Select the error state from Redux store
  const dispatch = useDispatch(); // Get the dispatch function

  useEffect(() => {
    // Simulate fetching user data from an API
    dispatch(setLoading(true)); // Set loading state to true
    axios.get('https://jsonplaceholder.typicode.com/users/1')
      .then(response => {
        dispatch(setUser(response.data)); // Dispatch setUser action with fetched user data
        dispatch(setLoading(false)); // Set loading state to false after data is fetched
      })
      .catch(error => {
        dispatch(setError(error.message)); // Dispatch setError action if an error occurs
        dispatch(setLoading(false)); // Set loading state to false if an error occurs
      });
  }, [dispatch]);

  return (
    <div>
      {loading && <p>Loading...</p>} 
      {error && <p>Error: {error}</p>} 
      {user && (
        <div>
          <h2>User Information</h2>
          <p>Name: {user.name}</p>
          <p>Email: {user.email}</p>
          <p>Phone: {user.phone}</p>
        </div>
      )}
    </div>
  );
};
export default App;

In the above code:

  • We use useSelector from react-redux to select specific parts of the Redux state (user, loading, error) in our component.

  • We use useDispatch from react-redux to get the dispatch function, which we'll use to dispatch actions to update the Redux state.

  • Inside the useEffect hook, we simulate fetching user data from an API using Axios.

  • We dispatch actions (setUser, setLoading, setError) based on the result of the API call to update the Redux state.

  • We conditionally render UI elements based on the Redux state (loading, error, user).

Now, we can use the App.jsx in our application to display user information fetched from the Redux store.

Today's journey into React-Redux and Redux Toolkit was enlightening. I gained insights into seamless state management with React-Redux, which empowers centralized store management for React applications. Additionally, Redux Toolkit proved transformative, simplifying development with its streamlined utilities. Hands-on experimentation led me to master the creation of Redux stores and slices, setting the stage for scalable and elegant application development. This exploration equips me with invaluable skills for crafting efficient solutions in future projects.

Thank You !!

ย