I'm creating a simple book list application using react and managing the state using redux. I've only 2 components that deal with the state, the input component dispatches the action and payload, while the output component should get the updated state data. Using the useSelector() hook inside the output component, I can see that the state is updated, however, when I try to access the array of objects and a length property, I get 'cannot read properties of undefined'. Please let me know what did I miss here.
Here is my code for Input Component
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import styles from "./Input.module.css";
const Input = () => {
const dispatch = useDispatch();
const [bookObject, setBookObject] = useState({
bookName: "",
authorName: "",
});
const inputChangeHandler = (e) => {
if (e.target.id === "bookName" || e.target.id === "authorName") {
setBookObject({
bookName: document.getElementById("bookName").value,
authorName: document.getElementById("authorName").value,
});
}
};
const submitHandler = (e) => {
if (bookObject.bookName !== "" && bookObject.authorName !== "") {
dispatch({
type: "GET_INPUT",
payload: {
name: bookObject.bookName,
author: bookObject.authorName,
id: Math.random(),
},
});
setBookObject({ bookName: "", authorName: "" });
} else {
alert("Enter valid Details");
}
e.preventDefault();
};
return (
<form className={styles.form} onSubmit={submitHandler}>
<div>
<label>Book's Name</label>
<input
id="bookName"
type="text"
placeholder="Enter the book's name"
onChange={inputChangeHandler}
value={bookObject.bookName}
/>
</div>
<div>
<label>Author's Name</label>
<input
id="authorName"
type="text"
placeholder="Enter the Author's name"
onChange={inputChangeHandler}
value={bookObject.authorName}
/>
</div>
<button>Submit</button>
</form>
);
};
export default Input;
Here is the code of the Output Component
import React, { Fragment } from "react";
import styles from "./Output.module.css";
import { useSelector } from "react-redux";
const Output = () => {
const outputState = useSelector((state) => state);
const length = outputState.length
const outputObj = outputState.outputObj
return (
<Fragment>
{length !== undefined ? (
<div className={styles.div}>
<h4>Book List</h4>
<ul>
{outputObj.map((book) => (
<li key={book.id}>
{book.name}, written by {book.author}
</li>
))}
</ul>
</div>
) : (
<h4>The Book List is empty</h4>
)}
</Fragment>
);
};
When I console log the outputState, I get a proper object with outputObj array and a length property, but when I try to access outputState.outputObj or outputState.length, I get the error mentioned. I've also tried using two useSelectors each to get the data separately, but in vain.
Here is the code of reducer
import { createStore } from "redux";
const defaultState = {
outputObj: [],
length: undefined,
};
const bookListReducer = (state = defaultState, action) => {
if (action.type === "GET_INPUT") {
state.outputObj = [...state.outputObj, action.payload];
return {
outputObj: state.outputObj,
length: state.outputObj.length,
};
}
};
const store = createStore(bookListReducer);
export default store;
If there was no action, or your action's type was not "GET_INPUT" your reducer will return undefined, therefore the state will be flushed. Update your code as follows.
const bookListReducer = (state = defaultState, action) => {
if (action.type === "GET_INPUT") {
state.outputObj = [...state.outputObj, action.payload];
return {
outputObj: state.outputObj,
length: state.outputObj.length,
};
}
return state; // <- HERE
};