Flashcards Challenge Project × TypeError: Cannot read property 'topics' of undefined

Hi everyone, Im doing this project and I am stuck on task 9:

https://www.codecademy.com/paths/full-stack-engineer-career-path/tracks/fscp-redux/modules/fscp-challenge-project-flashcards/projects/react-redux-flashcards

Task 9 is saying:

Next, you’ll need to hook the new topic form up to the action creators your slice generates. In src/components/NewTopicForm.js , import addTopic and dispatch it from the event handler that runs when the new topic form is submitted.

Verify that your code is working by filling out the form and submitting it. You should be redirected to the /topics page and should see your newly created topic there.

I should be able to see the topic I have written after this stage but Instead I am getting this error:
× TypeError: Cannot read property ‘topics’ of undefined

on this line

> 27 | export const selectTopics = (state) => state.topics.topics;

Here is my code where am I going wrong?

topicsSlice.js

import { createSlice } from '@reduxjs/toolkit';

export const topicsSlice = createSlice({

  name: 'topics',

  initialState: {

    topics: {}

  },

  reducers: {

    addTopic: (state, action) => {

        const { id, name, icon } = action.payload;

        state.topics[id] = {

            id: id,

            name: name,

            icon: icon,

            quizIds: []

        }

    },

  }

})

// console.log(topicsSlice);

export const {addTopic} = topicsSlice.actions

export const selectTopics = (state) => state.topics.topics;

// console.log(selectTopics)

export default topicsSlice.reducer;

Topics.js

import React from "react";

import NewTopicForm from "../../components/NewTopicForm";

import { Link } from "react-router-dom";

import ROUTES from "../../app/routes";

import { useSelector } from 'react-redux';

import { selectTopics } from './topicsSlice.js'

export default function Topics() {

  const topics = useSelector(selectTopics); // replace this with a call to your selector to select all the topics in state

  return (

    <section className="center">

      <h1>Topics</h1>

      <ul className="topics-list">

        {Object.values(topics).map((topic) => (

          <li className="topic" key={topic.id}>

          <Link to={ROUTES.topicRoute(topic.id)} className="topic-link">

           <div className="topic-container">

             <img src={topic.icon} alt="" />

             <div className="text-content">

               <h2>{topic.name}</h2>

               <p>{topic.quizIds.length} Quizzes</p>

             </div>

           </div>

         </Link>

          </li>

        ))}

      </ul>

      <Link

        to={ROUTES.newTopicRoute()}

        className="button create-new-topic-button"

      >

        Create New Topic

      </Link>

    </section>

  );

}

NewTopicForm.js

import React, { useState } from "react";

import { useHistory } from "react-router-dom";

import { v4 as uuidv4 } from "uuid";

import ROUTES from "../app/routes";

import { ALL_ICONS } from "../data/icons";

import { useDispatch } from 'react-redux';

import { addTopic } from '../features/topics/topicsSlice.js'

export default function NewTopicForm() {

  const dispatch = useDispatch();

  const [name, setName] = useState("");

  const [icon, setIcon] = useState("");

  const history = useHistory();

  const handleSubmit = (e) => {

    e.preventDefault();

    if (name.length === 0) {

      return;

    }

    // dispatch your add topic action here

    // dispatch(addTopic({id: uuidv4(), name: name, icon: icon}));

    dispatch(addTopic({ id: uuidv4(), name: name, icon: icon }));

    history.push(ROUTES.topicsRoute());

  };

  return (

    <section>

      <form onSubmit={handleSubmit}>

        <h1 className="center">Create a new topic</h1>

        <div className="form-section">

          <input

            id="topic-name"

            type="text"

            value={name}

            onChange={(e) => setName(e.currentTarget.value)}

            placeholder="Topic Name"

          />

          <select

            onChange={(e) => setIcon(e.currentTarget.value)}

            required

            defaultValue="default"

          >

            <option value="default" disabled hidden>

              Choose an icon

            </option>

            {ALL_ICONS.map(({ name, url }) => (

              <option key={url} value={url}>

                {name}

              </option>

            ))}

          </select>

        </div>

        <button className="center">Add Topic</button>

      </form>

    </section>

  );

}

store.js

import { configureStore } from "@reduxjs/toolkit";

import topicsReducer from '../features/topics/topicsSlice'

// console.log(topicsReducer)

export default configureStore({

  reducer: {

    topicsSlice: topicsReducer,

  },

});

I had the same issue! In ‘store.js’ replace “topicsSlice”

by “topics” and it should fix the error.

I am having the same issue but mine is coded a bit differently

here is the error again:

×
TypeError: Cannot read properties of undefined (reading 'topics')
selectTopic
src/features/topics/topicSlice.js:26
  23 | console.log(topicsSlice)
  24 | export const { addTopic, addQuizId } = topicsSlice.actions
  25 | export default topicsSlice.reducer;
> 26 | export const selectTopic = state => state.topics.topics

topicSlice.js

import { createSlice } from "@reduxjs/toolkit";

const topicsSlice = createSlice({
    name: 'topics',
    initialState: {topics: {}},
    reducers: {
      addTopic(state,action){
        return {...state,topics:{...state.topics,...action.payload}}
      },
      addQuizId(state,action){
        return {
          ...state,
          topics:{
            ...state.topics,
            quizId: []
          }
        }
      }

    }
  });
  console.log(topicsSlice)
  export const { addTopic, addQuizId } = topicsSlice.actions
  
  export const selectTopic = state => state.topics.topics

Topics.js

import NewTopicForm from "../../components/NewTopicForm";
import { Link } from "react-router-dom";
import ROUTES from "../../app/routes";
import { selectTopic } from "./topicSlice";
import { useSelector } from "react-redux";


export default function Topics() {
const topics =  useSelector(selectTopic)

 return (
    <section className="center">
      <h1>Topics</h1>
      <ul className="topics-list">
        {Object.values(topics).map((topic) => (
          <li className="topic" key={topic.id}>
          <Link to={ROUTES.topicRoute(topic.id)} className="topic-link">
           <div className="topic-container">
             <img src={topic.icon} alt="" />
             <div className="text-content">
               <h2>{topic.name}</h2>
               <p>{topic.quizIds.length} Quizzes</p>
             </div>
           </div>
         </Link>
          </li>
        ))}
      </ul>
      <Link
        to={ROUTES.newTopicRoute()}
        className="button create-new-topic-button"
      >
        Create New Topic
      </Link>
    </section>
  );
}

NewTopicForm.js

import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import { v4 as uuidv4 } from "uuid";
import ROUTES from "../app/routes";
import { ALL_ICONS } from "../data/icons";
import { addTopic } from "../features/topics/topicSlice";
import { useDispatch } from "react-redux";

export default function NewTopicForm() {
  const [name, setName] = useState("");
  const [icon, setIcon] = useState("");
  const history = useHistory();
  const dispatch = useDispatch();

  const handleSubmit = (e) => {
    e.preventDefault();
    if (name.length === 0) {
      return;
    }

    dispatch(addTopic({ id: uuidv4(), name: name, icon: icon }));
    history.push(ROUTES.topicsRoute());
  };

  return (
    <section>
      <form onSubmit={handleSubmit}>
        <h1 className="center">Create a new topic</h1>
        <div className="form-section">
          <input
            id="topic-name"
            type="text"
            value={name}
            onChange={(e) => setName(e.currentTarget.value)}
            placeholder="Topic Name"
          />
          <select
            onChange={(e) => setIcon(e.currentTarget.value)}
            required
            defaultValue="default"
          >
            <option value="default" disabled hidden>
              Choose an icon
            </option>
            {ALL_ICONS.map(({ name, url }) => (
              <option key={url} value={url}>
                {name}
              </option>
            ))}
          </select>
        </div>
        <button className="center">Add Topic</button>
      </form>
    </section>
  );
}

store.js

import { configureStore } from "@reduxjs/toolkit";
import Topics from "../features/topics/Topics";

export default configureStore({
  reducer: {
   
  },
});