Code To Learn logo

Code To Learn

M6: State ManagementRedux Toolkit Path

L4: Add Slice to Store

Register the listings slice in the Redux store

Now that we have a listings slice, let's register it in the store so Redux knows about it!

Current Store Setup

Currently, our store has an empty reducer object:

src/state/store.js
import { configureStore } from '@reduxjs/toolkit';

export const store = configureStore({
  reducer: {
    // Empty - no slices registered yet!
  },
});

What We're Building

We'll import the listings reducer and add it to the store's reducer configuration.

Step 1: Import Listings Reducer

At the top of src/state/store.js, import the listings reducer:

src/state/store.js
import { configureStore } from '@reduxjs/toolkit';
import listingsReducer from './slices/listingsSlice';

export const store = configureStore({
  reducer: {
    // Will add here next
  },
});

Note: We import the default export (the reducer), not the named exports (the actions).

Step 2: Add to Reducer Object

Add the listings reducer to the reducer configuration:

src/state/store.js
import { configureStore } from '@reduxjs/toolkit';
import listingsReducer from './slices/listingsSlice';

export const store = configureStore({
  reducer: {
    listings: listingsReducer,
  },
});

What's happening here?

Complete Code

Here's the complete updated store:

src/state/store.js
import { configureStore } from '@reduxjs/toolkit';
import listingsReducer from './slices/listingsSlice';

export const store = configureStore({
  reducer: {
    listings: listingsReducer,
  },
});

Simple and clean! Only 2 new lines to register a slice.

Verify in Redux DevTools

Let's check that our slice is registered correctly:

Open Redux DevTools

  1. Open your app in the browser
  2. Open browser DevTools (F12)
  3. Click the Redux tab

Check State Tab

Click the State tab. You should now see:

{
  "listings": {
    "items": [],
    "favorites": [],
    "status": "idle",
    "error": null
  }
}

Perfect! Our listings slice is registered! 🎉

Test an Action

In the Redux DevTools Dispatch section, manually dispatch an action:

{
  "type": "listings/toggleFavorite",
  "payload": 123
}

Click Dispatch and check the state again:

{
  "listings": {
    "items": [],
    "favorites": [123],  // ← 123 was added!
    "status": "idle",
    "error": null
  }
}

It works! Your reducer is handling actions! 🎉

Understanding State Access

Now that the slice is registered, here's how components access state:

Access entire listings slice:

import { useSelector } from 'react-redux';

function Component() {
  const listings = useSelector((state) => state.listings);
  
  console.log(listings);
  // {
  //   items: [],
  //   favorites: [],
  //   status: 'idle',
  //   error: null
  // }
}

The useSelector hook:

  • Takes a selector function: (state) => state.listings
  • Returns the selected value
  • Subscribes component to state changes
  • Re-renders when selected state changes

Access specific fields:

import { useSelector } from 'react-redux';

function Component() {
  const items = useSelector((state) => state.listings.items);
  const favorites = useSelector((state) => state.listings.favorites);
  const status = useSelector((state) => state.listings.status);
  
  console.log(items);      // []
  console.log(favorites);  // []
  console.log(status);     // 'idle'
}

Best practice: Select only what you need!

Why?

  • Component only re-renders when that specific field changes
  • Better performance
  • Clearer dependencies

Access multiple slices (future):

import { useSelector } from 'react-redux';

function Component() {
  const listings = useSelector((state) => state.listings);
  const user = useSelector((state) => state.user);
  const cart = useSelector((state) => state.cart);
  
  // Use data from multiple slices
  return (
    <div>
      <p>User: {user.name}</p>
      <p>Listings: {listings.items.length}</p>
      <p>Cart: {cart.items.length} items</p>
    </div>
  );
}

Each useSelector subscribes to one slice of state.

Common Patterns

Pattern 1: Select and Destructure

function Component() {
  const { items, favorites, status } = useSelector((state) => state.listings);
  
  return (
    <div>
      <p>Total: {items.length}</p>
      <p>Favorites: {favorites.length}</p>
      <p>Status: {status}</p>
    </div>
  );
}

Pros:

  • Clean syntax
  • Easy to read

Cons:

  • Re-renders when ANY field in listings changes
  • Less performant for large states

Pattern 2: Select Individual Fields

function Component() {
  const items = useSelector((state) => state.listings.items);
  const favorites = useSelector((state) => state.listings.favorites);
  
  return (
    <div>
      <p>Total: {items.length}</p>
      <p>Favorites: {favorites.length}</p>
    </div>
  );
}

Pros:

  • Only re-renders when specific fields change
  • Better performance

Cons:

  • More verbose
  • Multiple useSelector calls

Use Pattern 2 for performance-critical components!

Pattern 3: Create Selector Function

// In listingsSlice.js
export const selectListings = (state) => state.listings;
export const selectFavorites = (state) => state.listings.favorites;
export const selectIsLoading = (state) => state.listings.status === 'loading';

// In component
import { selectFavorites, selectIsLoading } from '@/state/slices/listingsSlice';

function Component() {
  const favorites = useSelector(selectFavorites);
  const isLoading = useSelector(selectIsLoading);
  
  // ...
}

Pros:

  • Reusable selectors
  • DRY principle
  • Easy to test
  • Can compute derived state

Recommended for real apps!

Redux State Tree

With our slice registered, the state tree looks like:

Redux Store
 └─ listings (from listingsReducer)
     ├─ items: []
     ├─ favorites: []
     ├─ status: 'idle'
     └─ error: null

As you add more slices:

Redux Store
 ├─ listings (from listingsReducer)
 │   ├─ items: []
 │   ├─ favorites: []
 │   ├─ status: 'idle'
 │   └─ error: null
 ├─ user (from userReducer)
 │   ├─ profile: null
 │   └─ isAuthenticated: false
 └─ cart (from cartReducer)
     ├─ items: []
     └─ total: 0

Each slice is isolated but part of the same store!

Troubleshooting

Issue: "Cannot read property 'items' of undefined"

Problem: Trying to access state before slice is registered

const items = useSelector((state) => state.listings.items);
// Error: Cannot read property 'items' of undefined

Solution: Make sure slice is added to store:

// store.js
reducer: {
  listings: listingsReducer,  // ← Must be here!
}

Issue: "useSelector is not a function"

Problem: Not importing from 'react-redux'

import { useSelector } from 'redux';  // ❌ Wrong package!

Solution: Import from 'react-redux':

import { useSelector } from 'react-redux';  // ✅ Correct!

Issue: State not updating in DevTools

Problem: Not dispatching actions correctly

Solution: Make sure you're using dispatch:

import { useDispatch } from 'react-redux';
import { toggleFavorite } from '@/state/slices/listingsSlice';

function Component() {
  const dispatch = useDispatch();
  
  const handleClick = () => {
    dispatch(toggleFavorite(123));  // ✅ Correct!
  };
}

What's Next?

Perfect! The listings slice is now registered in the store. In the next lesson, we'll:

  1. Create an async thunk - Fetch listings from the API
  2. Dispatch async actions - Call the API with Redux
  3. Handle loading states - Track fetch status

✅ Lesson Complete! Your listings slice is registered and visible in Redux DevTools!

Key Takeaways

  • ✅ Register slices by adding them to reducer object
  • ✅ Key name determines state access: state.listings
  • ✅ Import the default export (reducer function)
  • ✅ Use Redux DevTools to verify registration
  • ✅ Select specific fields with useSelector for better performance
  • ✅ Create selector functions for reusable state access