How to capture logged user in the array of users? - reactjs

in my CRUD app, the user on the first screen enters their username and then can create, edit, and delete posts. I have an array of users and an array of posts (I'm using Redux). When a user creates a post, their username needs to appear in a sector within the div to identify who posted the post, but I still can't find a way to get the right user who is logged in to show in the div. How could I capture the specific user who is currently logged in?
I thought of doing something like: when the user typed their username and went to the posts screen, all posts would be linked to this user, but I don't know how to do that
postsSlice.js:
import { createSlice } from "#reduxjs/toolkit";
const postsSlice = createSlice({
name: "posts",
initialState: [],
reducers: {
addPost (state, action) {
state.push(action.payload);
},
editPost(state, action) {
const { id, title, content } = action.payload;
const existingPost = state.find((post) => post.id === id);
if (existingPost) {
existingPost.title = title
existingPost.content = content
}
},
postDeleted(state, action) {
const { id } = action.payload;
const existingPost = state.some((post) => post.id === id);
if (existingPost) {
return state.filter((post) => post.id !== id);
}
},
},
});
export const { addPost, editPost, postDeleted } = postsSlice.actions
export default postsSlice
usersSlice.js:
import { createSlice } from "#reduxjs/toolkit";
const userSlice = createSlice({
name: "user",
initialState: [],
reducers: {
saveUser (state, action) {
state.push(action.payload)
},
}
});
export const { saveUser, replaceUsers } = userSlice.actions;
export default userSlice
mainscreen.js:
import React, { useState, useEffect } from "react";
import "../_assets/App.css";
import "../_assets/mainscreen.css";
import { MdDeleteForever } from "react-icons/md";
import { FiEdit } from "react-icons/fi";
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { addPost } from '../redux/postsslice'
import Modal from "../components/modal.jsx";
import EditModal from '../components/editmodal.jsx';
function MainScreen() {
const dispatch = useDispatch();
const navigate = useNavigate();
const user = useSelector((state) => state.user)
const posts = useSelector((state) => state.loadPosts)
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [buttonGreyOut, setButtonGreyOut] = useState("#cccccc");
useEffect(() => {
if (title && content !== "") {
setButtonGreyOut("black");
} else {
setButtonGreyOut("#cccccc");
}
},[title, content]);
const handleSubmitSendPost = (e) => {
e.preventDefault();
dispatch(
addPost({
id: new Date(),
title,
content,
user
})
)
setTitle('')
setContent('')
};
const handleChangeTitle = (text) => {
setTitle(text);
};
const handleChangeContent = (text) => {
setContent(text);
};
const handleButton = (e) => {
e.preventDefault()
navigate("/")
}
const [openEditModal, setOpenEditModal] = useState();
const [openModal, setOpenModal] = useState();
console.log({ posts })
return (
<div className="containerMainScreen">
<button onClick={handleButton}>Back</button>
{openModal && <Modal deleteId={openModal} closeModal={setOpenModal} />}
{openEditModal && <EditModal editId={openEditModal} closeModal={setOpenEditModal} />}
<div className="bar">
<h1>Codeleap</h1>
</div>
<div className="boxPost">
<h2 style={{ fontWeight: 700 }}>What's on your mind?</h2>
<h2>Title</h2>
<form onSubmit={handleSubmitSendPost}>
<input
type="text"
placeholder="Hello World"
name="name"
value={title}
onChange={(e) => handleChangeTitle(e.target.value)}
></input>
<h2>Content</h2>
<textarea
placeholder="Content"
name="content"
value={content}
onChange={(e) => handleChangeContent(e.target.value)}
></textarea>
<button
className="createButton"
type="submit"
style={{ backgroundColor: buttonGreyOut }}
disabled={!title || !content}
>
CREATE
</button>
</form>
</div>
{posts.slice().reverse().map((post) => (
<div className="boxPost" key={post.id}>
<div className="bar">
<h1>{post.title}</h1>
<MdDeleteForever
className="icon"
onClick={() => {
setOpenModal(post.id);
}}
/>
<FiEdit
onClick={() => {
setOpenEditModal(post.id);
}}
style={{ color: "white", fontSize: "45px", paddingLeft: "23px" }}
/>
</div>
<div id="postowner">
<h3>#{user}</h3>
<br></br>
<textarea style={{ border: "none" }} value={post.content}></textarea>
</div>
</div>
))}
</div>
);
}
export default MainScreen;
Signup:
import React, {useState, useEffect} from "react";
import "../_assets/signup.css";
import "../_assets/App.css";
import { useDispatch } from 'react-redux';
import userSlice from '../redux/userslice';
import { useNavigate } from "react-router-dom";
function Signup() {
const navigate = useNavigate();
const dispatch = useDispatch();
const [name, setName] = useState('')
const [buttonGrey, setButtonGrey] = useState('#cccccc')
useEffect(() => {
if (name!== '') {
setButtonGrey("black")
}
else {
setButtonGrey('#cccccc')
}
}, [name])
const handleSubmitForm= (e) => {
e.preventDefault()
dispatch(userSlice.actions.saveUser(name));
navigate("/main")
}
const handleChangeName = (text) => {
setName(text)
}
return (
<div className="container">
<div className="LoginBox">
<form onSubmit={handleSubmitForm}>
<h2>Welcome to codeleap network</h2>
<text>Please enter your username</text>
<input type="text" name="name" value={name} onChange = {e => handleChangeName(e.target.value)} placeholder="Jane Doe" />
<div className="button">
<button type="submit" style={{backgroundColor: buttonGrey}} disabled={!name} >
ENTER
</button>
</div>
</form>
</div>
</div>
);
}
export default Signup;

You can store a current user and maintain a list of all users that ever "signed in".
user.slice.js
const initialState = {
currentUser: '',
users: [],
}
const userSlice = createSlice({
name: "user",
initialState,
reducers: {
saveUser(state, action) {
// person that just signed in
state.currentUser = action.payload;
// list of unique user names
state.users = Array.from(new Set([...state.users, action.payload]));
},
replaceUsers: (state, action) => action.payload
}
});
export const { saveUser } = userSlice.actions;
export default userSlice;
MainScreen.js
Add an author property to each added post, and access the post.author property when mapping the posts.
function MainScreen() {
const dispatch = useDispatch();
const { currentUser } = useSelector((state) => state.user); // <-- current user
const posts = useSelector((state) => state.loadPosts);
...
const handleSubmitSendPost = (e) => {
e.preventDefault();
dispatch(
addPost({
id: uuidV4(),
title,
content,
author: currentUser // <-- current user to post
})
);
setTitle("");
setContent("");
};
...
if (currentUser === "") {
return <Navigate to="/" />;
} else {
return (
<div className="containerMainScreen">
...
{posts.map((post) => (
<div className="boxPost" key={post.id}>
<div className="bar">
<h1>{post.title}</h1>
<MdDeleteForever
className="icon"
onClick={() => {
setOpenModal(post.id);
}}
/>
<FiEdit
onClick={() => {
setOpenEditModal(post.id);
}}
style={{
color: "white",
fontSize: "45px",
paddingLeft: "23px"
}}
/>
</div>
<div id="postowner">
<h3>#{post.author}</h3> // <-- user in post object
<br></br>
<textarea
style={{ border: "none" }}
value={post.content}
></textarea>
</div>
</div>
))}
</div>
);
}
}
export default MainScreen;

Related

i want to prefill form data when update button is clicked.update is working but it does not show the data which one i want to update

I tried using it with the useState hook. singlebook['title'] and singlebook['author'] show right data in the console but not show in form data as prefill data. I am new in the next js.i did it with react and useLocation hook before.but useLocation is not working in next js. if i use value=singlebook['title'] inside the input tag. i can not clicked it and erase that data and can not edit anything
[editBookId].tsx
import React, { useState} from "react";
import { useRouter } from "next/router";
import { useDispatch} from "react-redux";
import { useAppDispatch, useAppSelector } from "../../hooks";
import { getAllBooks, updateBook} from "../../slices/bookSlice";
import { useForm } from "react-hook-form";
import { ToastContainer } from "react-toastify";
const EditBook = () => {
const AllBooks = useAppSelector(getAllBooks);
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const dispatch = useDispatch();
const router = useRouter();
let editBookId = router.query.editBookId
console.log(editBookId);
//return;
let singlebook='';
AllBooks['books'].map((x)=>{
if(editBookId==x.id){
singlebook=x;
}
}
)
const [data, setData] = useState({
title: singlebook['title'],
author: singlebook['author'],
});
const handleChange = (e) => {
setData({ ...data, [e.target.name]: e.target.value });
};
//const single book = AllBooks['books'];
console.log(singlebook)
const onSubmit = (e) => {
let data={'id':singlebook['id'],...e};
console.log(data)
dispatch(updateBook(data));
router.push("/showBooks");
};
return (
<div>
<ToastContainer/>
<h1 className="text-center">Edit Book</h1>
<div className="d-flex justify-content-center">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="mb-3">
<label htmlFor="text" className="form-label">
Title
</label>
<input
{...register("title", { required: "Title is required" })}
type="text"
name="title"
// value={singlebook['title']}
onChange={handleChange}
className="form-control"
/>
{errors.title?.type === "required" && "Title is required" && (
<p style={{ color: "red" }}>Title is Required.</p>
)}
</div>
<div className="mb-3">
<label htmlFor="text" className="form-label">
Author
</label>
<input
{...register("author", { required: "Author is required" })}
type="text"
name="author"
// value={singlebook['author']}
onChange={handleChange}
className="form-control"
/>
{errors.author?.type === "required" && "Author is required" && (
<p style={{ color: "red" }}>Author is Required.</p>
)}
</div>
<button type="submit" className="btn btn-primary">
Update Book
</button>
</form>
</div>
</div>
);
};
export default EditBook;
every functionality of crud operation is implemented here.all create, read, delete, update is working.
bookSlice.ts
import { PayloadAction } from "#reduxjs/toolkit";
import { toast } from "react-toastify";
const { createSlice } = require("#reduxjs/toolkit");
import { v4 as uuidv4 } from "uuid";
import { RootState } from "../store";
type Book = {
id: string;
title: string;
author: string;
};
type initialState = {
books: Book[];
};
const initialState: initialState = {
books: [
{ id: uuidv4(), title: "new programmer", author: "john smith" },
{ id: uuidv4(), title: "Connecting the dot", author: "john lilly" },
],
};
export const booksSlice = createSlice({
name: "books",
initialState: initialState,
reducers: {
showBooks: (state) => state,
addBook: (state, action: PayloadAction<Book[]>) => {
state.books.push(action.payload);
toast.success("New Book Added");
},
updateBook: (state, action: PayloadAction<Book>) => {
const { id, title, author } = action.payload;
const isBookExist = state.books.filter((book) => book.id === id);
if (isBookExist) {
isBookExist[0].title = title;
isBookExist[0].author = author;
toast.info("Book Updated");
}
},
deleteBook: (state, action: PayloadAction<Book>) => {
const id = action.payload;
state.books = state.books.filter((book) => book.id !== id);
toast.warn("Book Deleted");
},
},
});
export const { showBooks, addBook, deleteBook, updateBook } =
booksSlice.actions;
export const getAllBooks = (state: RootState) => state.books;
export default booksSlice.reducer;

how can i open and close components with onClick and dispatch close on out click with redux toolkit?

i create my reducers with createSlice in redux toolkit i declared my states
this is my store in ./root/app/store.js
import { configureStore } from "#reduxjs/toolkit";
import calendarReducer from '../features/calendarSlice'
import windowReducer from '../features/windowSlice'
import startReducer from '../features/startSlice'
export const store = configureStore({
reducer: {
calendar: calendarReducer,
window: windowReducer,
start: startReducer
},
})
and this is my calendarSlice in ./root/features/calendarSlice.jsx
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
value: false
}
export const calendarSlice = createSlice({
name: "calendar",
initialState,
reducers: {
openCalendar : (state) => {
state.value = true
},
closeCalendar: (state) => {
state.value = false
},
closeCalendarOnOtherLayoutsClick: (state) => {
state.value = false
}
}
});
export const {openCalendar, closeCalendar} = calendarSlice.actions
export default calendarSlice.reducer
and this is my startSlice in ./root/features/startSlice.jsx
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
value: false
}
export const startSlice = createSlice({
name:'start',
initialState,
reducers: {
openStart: (state) => {
state.value = true
},
closeStart: (state) => {
state.value = false
}
}
});
export const {openStart, closeStart} = startSlice.actions
export default startSlice.reducer
and below code is my main component with imported component such as calendar and start
my code works perfect when click on clock calendar component state = true and open when click again calendar get close but when i click on calendar it close himself i want active this features in redux reducer with actions.
can anybody answer how can i do this project in new redux.
import styles from "./dock.module.scss";
import { useRef } from "react";
import Image from "next/image";
import { openCalendar,closeCalendar } from '../../../features/calendarSlice'
import { openStart, closeStart } from '../../../features/startSlice'
import { useDispatch, useSelector } from 'react-redux'
import Start from '../start/Start'
import Calendar from "./Calendar";
const Dock = () => {
const dispatch = useDispatch()
const leftDock = useRef(null)
const centerDock = useRef(null)
const rightDock = useRef(null)
const calendarState = useSelector((state) => state.calendar.value)
const startState = useSelector((state) => state.start.value)
const calendarHandler = () => {
if(calendarState == true){
dispatch(closeCalendar())
}else {
dispatch(openCalendar())
}
}
const startHandler = () => {
if(startState == true){
dispatch(closeStart())
}else {
dispatch(openStart())
}
}
const dockClickHandler = (e) => {
e.stopPropagation()
dispatch(closeCalendar())
}
return (
<div className={styles.dockContainer}>
<div onClick={dockClickHandler} ref={leftDock} className={styles.left}></div>
<div onClick={dockClickHandler} ref={centerDock} className={styles.center}>
<div onClick={startHandler} className={styles.iconContainer}>
<div className={styles.icon}>
<Image src="/icon/home.png" width={24} height={24} />
</div>
</div>
<div className={styles.iconContainer}>
<div className={styles.icon}>
<Image src="/icon/search.png" width={24} height={24} />
</div>
</div>
<div className={styles.iconContainer}>
<div className={styles.icon}>
<Image src="/icon/settings.png" width={24} height={24} />
</div>
</div>
<div className={styles.iconContainer}>
<div className={styles.icon}>
<Image src="/icon/explorer.png" width={24} height={24} />
</div>
</div>
<div className={styles.iconContainer}>
<div className={styles.icon}>
<Image src="/icon/store.png" width={24} height={24} />
</div>
</div>
<div className={styles.iconContainer}>
<div className={styles.icon}>
<Image src="/icon/spotify.png" width={24} height={24} />
</div>
</div>
</div>
<div ref={rightDock} onClick={calendarHandler}
className={styles.right}>
12:00
<Calendar calendarState={calendarState} />
</div>
<Start startState={startState} />
</div>
);
};
export default Dock;
const calendar = {
position: "absolute",
width: "21rem",
height: "300px",
maxWidth: "500px",
maxHeight: "500px",
right: "12px",
bottom: "calc(48px + 12px)",
borderRadius: "8px",
backgroundColor: "red",
zIndex: "9999",
color: "black",
overflow: "hidden",
transition: "1s ease",
};
a view of open calendar
here it's my issue when i have start open and click on calendar start should be close without dispatch confilicts
You need to add payload in your slices then dispatch your value in your component
calendarSlice.jsx
export const calendarSlice = createSlice({
name: "calendar",
initialState: {
value: false
},
reducers: {
openCalendar: (state, action) => {
state.value = action.payload
},
// or with destructuring action
openCalendar: (state, { payload }) => {
state.value = payload
},
}
});
your component.jsx
const handleClick = (e) => {
e.preventDefault();
dispatch(openCalendar(!value));
};
Check my simple demo here : Stackblitz

Set a default content from database in react-draft-wysiwyg editor

I am creating a blog website in which I am embedding react-draft-wysiwyg editor. I am facing problem when the user has to update the blog. When I click the update button the content is gone. I looked into many solutions but I couldn't make it work.
This is my code
import axios from "axios";
import React, { useContext, useEffect, useState } from "react";
import { useLocation } from "react-router";
import { Link } from "react-router-dom";
import { Context } from "../../context/Context";
import "./singlePost.css";
import { EditorState, ContentState, convertFromHTML } from 'draft-js';
import { Editor } from 'react-draft-wysiwyg';
import { convertToHTML } from 'draft-convert';
import DOMPurify from 'dompurify';
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
import Parser from 'html-react-parser';
export default function SinglePost() {
const location = useLocation();
const path = location.pathname.split("/")[2];
const [post, setPost] = useState({});
const PF = "http://localhost:5000/images/";
const { user } = useContext(Context);
const [title, setTitle] = useState("");
const [desc, setDesc] = useState("");
const [updateMode, setUpdateMode] = useState(false);
useEffect(() => {
const getPost = async () => {
const res = await axios.get("/posts/" + path);
setPost(res.data);
setTitle(res.data.title);
setDesc(res.data.desc);
};
getPost();
}, [path]);
const handleDelete = async () => {
try {
await axios.delete(`/posts/${post._id}`, {
data: { username: user.username },
});
window.location.replace("/");
} catch (err) {}
};
// updating post
const handleUpdate = async () => {
try {
await axios.put(`/posts/${post._id}`, {
username: user.username,
title,
desc,
});
setUpdateMode(false)
} catch (err) {}
};
const [editorState, setEditorState] = useState(
() => EditorState.createWithContent(
ContentState.createFromBlockArray(
convertFromHTML(desc)
)
),
);
const [convertedContent, setConvertedContent] = useState(null);
const handleEditorChange = (state) => {
setEditorState(state);
convertContentToHTML();
}
const convertContentToHTML = () => {
let currentContentAsHTML = convertToHTML(editorState.getCurrentContent());
setConvertedContent(currentContentAsHTML);
setDesc(currentContentAsHTML);
}
const createMarkup = (html) => {
return {
__html: DOMPurify.sanitize(html)
}
}
return (
<div className="singlePost">
<div className="singlePostWrapper">
{post.photo && (
<img src={PF + post.photo} alt="" className="singlePostImg" />
)}
{updateMode ? (
<input
type="text"
value={title}
className="singlePostTitleInput"
autoFocus
onChange={(e) => setTitle(e.target.value)}
/>
) : (
<h1 className="singlePostTitle">
{title}
{post.username === user?.username && (
<div className="singlePostEdit">
<i
className="singlePostIcon far fa-edit"
onClick={() => setUpdateMode(true)}
></i>
<i
className="singlePostIcon far fa-trash-alt"
onClick={handleDelete}
></i>
</div>
)}
</h1>
)}
<div className="singlePostInfo">
<span className="singlePostAuthor">
Author:
<Link to={`/?user=${post.username}`} className="link">
<b> {post.username}</b>
</Link>
</span>
<span className="singlePostDate">
{new Date(post.createdAt).toDateString()}
</span>
</div>
{updateMode ? (
// <textarea
// className="singlePostDescInput"
// value={desc}
// onChange={(e) => setDesc(e.target.value)}
// />
<Editor
contentState={desc}
editorState={editorState}
onEditorStateChange={handleEditorChange}
wrapperClassName="wrapper-class"
editorClassName="editor-class"
toolbarClassName="toolbar-class"
/>
) : (
<p className="singlePostDesc">{Parser(desc)}</p>
)}
{updateMode && (
<button className="singlePostButton" onClick={handleUpdate}>
Update
</button>
)}
</div>
</div>
);
}
I want to display desc which is saved in MongoDB database when the user clicks on update button.
The following part is what I tried to do but didn't work.
const [editorState, setEditorState] = useState(
() => EditorState.createWithContent(
ContentState.createFromBlockArray(
convertFromHTML(desc)
)
),
);
I am getting warning in this:
react.development.js:220 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the r component.
Please help

Cannot read properties of undefined (reading 'find')

I'm doing a CRUD with redux. The application has a form with a username, and then a screen to create, view, edit and delete posts and I'm doing the editing part. However, when I use array.find() to traverse the array and look for each post, I'm getting this error:
Cannot read properties of undefined (reading 'find')
I will leave the codes below.
EditScreen:
import React, {useState} from 'react';
import '../_assets/modal.css';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation, useNavigate } from 'react-router-dom'
import {editPost} from '../redux/postsslice';
function EditModal({ closeModal}) {
const { pathname } = useLocation();
const postId = pathname.replace("/edit-post/", "")
const post = useSelector((state) => state.posts.find((post) => post.id === postId))
const dispatch = useDispatch()
const navigation = useNavigate();
const [title, setTitle] = useState(post.title)
const [content, setContent] = useState(post.content)
const onTitleChanged = e => setTitle(e.target.value)
const onContentChanged = e => setContent(e.target.value)
const onSavePostClicked = (e) => {
if (title && content) {
dispatch(editPost({id: postId, title, content}))
}
}
return (
<div className="modalBackground">
<div className="modalContainer">
<div className="title"><h1>Edit item</h1></div>
<h2>Title</h2>
<form>
<input
type="text"
placeholder="Hello World"
name="name"
value={title}
onChange={onTitleChanged}
></input>
<h2>Content</h2>
<textarea
placeholder="Content"
name="content"
value={content}
onChange={onContentChanged}
></textarea>
<button onClick={onSavePostClicked}>SAVE</button></form>
</div>
</div>
)
}
export default EditModal
mainscreen:
import React, { useState, useEffect } from "react";
import "../_assets/App.css";
import "../_assets/mainscreen.css";
import { MdDeleteForever } from "react-icons/md";
import { FiEdit } from "react-icons/fi";
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import { Navigate } from 'react-router-dom';
import { addPost } from '../redux/postsslice'
import {nanoid} from 'nanoid'
import Modal from "../components/modal.jsx";
import EditModal from '../components/editmodal.jsx';
function MainScreen() {
const dispatch = useDispatch();
const user = useSelector((state) => state.user)
const posts = useSelector((state) => state.loadPosts)
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [buttonGreyOut, setButtonGreyOut] = useState("#cccccc");
useEffect(() => {
if (title && content !== "") {
setButtonGreyOut("black");
} else {
setButtonGreyOut("#cccccc");
}
},[title, content]);
const handleSubmitSendPost = (e) => {
e.preventDefault();
dispatch(
addPost({
id: nanoid(),
title,
content
})
)
setTitle('')
setContent('')
};
const handleChangeTitle = (text) => {
setTitle(text);
};
const handleChangeContent = (text) => {
setContent(text);
};
const [openEditModal, setOpenEditModal] = useState();
const [openModal, setOpenModal] = useState();
if (user === '') {
return <Navigate to="/" />
} else {
return (
<div className="containerMainScreen">
{openModal && <Modal closeModal={setOpenModal} />}
{openEditModal && <EditModal closeModal={setOpenEditModal} />}
<div className="bar">
<h1>Codeleap</h1>
</div>
<div className="boxPost">
<h2 style={{ fontWeight: 700 }}>What's on your mind?</h2>
<h2>Title</h2>
<form onSubmit={handleSubmitSendPost}>
<input
type="text"
placeholder="Hello World"
name="name"
value={title}
onChange={(e) => handleChangeTitle(e.target.value)}
></input>
<h2>Content</h2>
<textarea
placeholder="Content"
name="content"
value={content}
onChange={(e) => handleChangeContent(e.target.value)}
></textarea>
<button
className="createButton"
type="submit"
style={{ backgroundColor: buttonGreyOut }}
disabled={!title || !content}
>
CREATE
</button>
</form>
</div>
{posts.map((post) => (
<div className="boxPost" key={post.id}>
<div className="bar">
<h1>{post.title}</h1>
<MdDeleteForever
className="icon"
onClick={() => {
setOpenModal(true);
}}
/>
<FiEdit
onClick={() => {
setOpenEditModal(true);
}}
style={{ color: "white", fontSize: "45px", paddingLeft: "23px" }}
/>
</div>
<div id="postowner">
<h3>#{user}</h3>
<br></br>
<textarea style={{ border: "none" }}>{post.content}</textarea>
</div>
</div>
))}
</div>
);
}
}export default MainScreen;
postSlice:
import { createSlice } from "#reduxjs/toolkit";
const postsSlice = createSlice({
name: "posts",
initialState: [],
reducers: {
addPost (state, action) {
state.push(action.payload); // modifies the draft state.
},
editPost(state, action) {
const { id, title, content } = action.payload;
const existingPost = state.find((post) => post.id === id);
if (existingPost) {
existingPost.title = title
existingPost.content = content
}
}
}
});
export const { addPost, editPost } = postsSlice.actions
export default postsSlice
editModal.js (edit page)
import React, {useState} from 'react';
import '../_assets/modal.css';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation, useNavigate } from 'react-router-dom'
import {editPost} from '../redux/postsslice';
export function EditModal({ closeModal}) {
const { pathname } = useLocation();
const postId = parseInt(pathname.replace("edit-post/", ""))
const post = useSelector((state) => state.loadPosts.find((post) => post.id === postId))
const dispatch = useDispatch()
const navigation = useNavigate();
const [title, setTitle] = useState(post.title)
const [content, setContent] = useState(post.content)
const onTitleChanged = e => setTitle(e.target.value)
const onContentChanged = e => setContent(e.target.value)
const onSavePostClicked = (e) => {
if (title && content) {
dispatch(editPost({id: postId, title, content}))
}
}
return (
<div className="modalBackground">
<div className="modalContainer">
<div className="title"><h1>Edit item</h1></div>
<h2>Title</h2>
<form>
<input
type="text"
placeholder="Hello World"
name="name"
value={title}
onChange={onTitleChanged}
></input>
<h2>Content</h2>
<textarea
placeholder="Content"
name="content"
value={content}
onChange={onContentChanged}
></textarea>
<button onClick={onSavePostClicked}>SAVE</button></form>
</div>
</div>
)
}
export default EditModal
store.js:
import { configureStore } from '#reduxjs/toolkit';
import userSlice from './userslice';
import postsSlice from './postsslice'
const store = configureStore({
reducer: {
user: userSlice.reducer,
loadPosts: postsSlice.reducer
},
})
export default store
signup page:
import React, {useState, useEffect} from "react";
import "../_assets/signup.css";
import "../_assets/App.css";
import { useDispatch } from 'react-redux';
import userSlice from '../redux/userslice';
import { useNavigate } from "react-router-dom";
function Signup() {
const navigate = useNavigate();
const dispatch = useDispatch();
const [name, setName] = useState('')
const [buttonGrey, setButtonGrey] = useState('#cccccc')
useEffect(() => {
if (name!== '') {
setButtonGrey("black")
}
else {
setButtonGrey('#cccccc')
}
}, [name])
const handleSubmitForm= (e) => {
e.preventDefault()
dispatch(userSlice.actions.saveUser(name))
navigate("/main")
}
const handleChangeName = (text) => {
setName(text)
}
return (
<div className="container">
<div className="LoginBox">
<form onSubmit={handleSubmitForm}>
<h2>Welcome to codeleap network</h2>
<text>Please enter your username</text>
<input type="text" name="name" value={name} onChange = {e => handleChangeName(e.target.value)} placeholder="Jane Doe" />
<div className="button">
<button type="submit" style={{backgroundColor: buttonGrey}} disabled={!name} >
ENTER
</button>
</div>
</form>
</div>
</div>
);
}
export default Signup;
With the state shape:
const store = configureStore({
reducer: {
user: userSlice.reducer,
loadPosts: postsSlice.reducer
},
});
Then the path to the posts state is state.loadPosts, so the selector in EditModal should be:
const post = useSelector((state) => state.loadPosts.find(
(post) => post.id === postId),
);
The error TypeError: Cannot read properties of undefined (reading 'title') is closely related to this line of code. If state.loadPosts is an empty array or there are not matching posts by id, then .find returns undefined.
const [title, setTitle] = useState(post.title); // <-- throws error on undefined post
const [content, setContent] = useState(post.content); // <-- throws error on undefined post
A quick fix could be to use the Optional Chaining operator
const [title, setTitle] = useState(post?.title);
const [content, setContent] = useState(post?.content);
but this only sets the initial state. If there is no matching post to edit then there's no point in rendering the editing UI. At this point you should render some fallback UI or navigate back, etc...

why my useReducer function is not working.when I use it The page show but function is not working

Hello Everyone . I'm New in react .Every thing is okay But when I'm start using Reducer. It's Not working properly. It's Adding function or deleting function is not working. Why This problem. before using reducer I'm working with context api . and the same project I want work with reducer. but it's not working properly. The page is show but It's Add or remove function is not working
BookContext.jsx
import React, { createContext, useReducer, useEffect } from 'react';
// import { v4 as uuid } from 'uuid' // This is update vertion
import { bookReducer } from '../BookReducer/Reducer';
export const BookContext = createContext();
const BookContextProvider = (props) => {
const [books, dispatch] = useReducer(bookReducer, []);
useEffect(() => {
localStorage.setItem('books', JSON.stringify(books))
}, [books])
return (
<div>
<BookContext.Provider value={{ books, dispatch }}>
{props.children}
</BookContext.Provider>
</div>
);
};
export default BookContextProvider;
Reducer.jsx
import { v4 as uuid } from 'uuid' // This is update vertion
export const bookReducer = (state, action) => {
switch (action.type) {
case ' ADD_BOOK':
return [...state, {
title: action.book.title,
author: action.book.author,
id: uuid()
}]
case 'REMOVE_BOOK':
return state.filter(book => book.id !== action.id)
default:
return state;
}
}
BookForm.jsx
import React, { useContext, useState } from 'react';
import { BookContext } from '../Context/BookContext';
const NewBookform = () => {
const { dispatch } = useContext(BookContext);
const [title, setitle] = useState('');
const [author, setauthor] = useState('');
const Handelesubmit = (e) => {
e.preventDefault()
dispatch({
type: 'ADD_BOOK', book: {
title, author
}
})
setitle(' ');
setauthor(' ');
}
return (
<div>
<form onSubmit={Handelesubmit}>
<input type='text' placeholder='Book Title' value={title} onChange
={(e) => setitle(e.target.value)} required />
<br></br>
<input type='text' placeholder='Book author' value={author} onChange
={(e) => setauthor(e.target.value)} required />
<br></br>
<input type='submit' value=" ADD Book" />
</form>
</div>
);
};
export default NewBookform;
Bookdetails
import React, { useContext } from 'react';
import { BookContext } from '../Context/BookContext';
const Bookldetails = ({ book }) => {
const { dispatch } = useContext(BookContext)
return (
<li onClick={() => dispatch({ type: 'REMOVE_BOOK', id: book.id })}>
<div className="title">{book.title}</div>
<div className="id">{book.author}</div>
</li>
);
};
export default Bookldetails;
Your remove function works just fine, here is your code:
const { createContext, useReducer, useContext } = React;
const uuid = ((id) => () => ++id)(0);
const BookContext = createContext();
const bookReducer = (state, action) => {
switch (action.type) {
case ' ADD_BOOK':
return [
...state,
{
title: action.book.title,
author: action.book.author,
id: uuid(),
},
];
case 'REMOVE_BOOK':
return state.filter((book) => book.id !== action.id);
default:
return state;
}
};
const BookContextProvider = (props) => {
const [books, dispatch] = useReducer(bookReducer, [
{ id: uuid(), title: 'title 1', author: 'author 1' },
{ id: uuid(), title: 'title 2', author: 'author 2' },
]);
//this is not doing anything, you are never reading it
// useEffect(() => {
// localStorage.setItem('books', JSON.stringify(books));
// }, [books]);
return (
<div>
<BookContext.Provider value={{ books, dispatch }}>
{props.children}
</BookContext.Provider>
</div>
);
};
const Bookldetails = ({ book }) => {
const { dispatch } = useContext(BookContext);
return (
<li
onClick={() =>
dispatch({ type: 'REMOVE_BOOK', id: book.id })
}
>
<div className="title">{book.title}</div>
<div className="id">{book.author}</div>
</li>
);
};
const App = () => {
const { books } = useContext(BookContext);
return (
<ul>
{books.map((book) => (
<Bookldetails key={book.id} book={book} />
))}
</ul>
);
};
ReactDOM.render(
<BookContextProvider>
<App />
</BookContextProvider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Resources