how to test react custom hook function call on button click - jestjs

I am having a problem testing the button click event. In test cases, toHaveBeenCalled returns 0.
Here below is my component file:
index.js (component file)
import { Button, ButtonThemes } from 'stitcht-ui';
import useReels from './hooks/useReels';
import { ReelCard } from '../../components/ReelCard';
import styles from './dashboard.module.scss';
import CreateReelButton from '../../components/CreateReelButton';
const Dashboard = () => {
const {
reels,
fetchMoreReels,
loading,
loadMoreRef,
allReelsLoaded,
threadDeleted,
} = useReels();
const navigate = useNavigate();
const reelOnClick = (reelId) => navigate(`/reel/${reelId}`);
if (!reels) {
return (
<div className={styles.reels}>
<ReelCard loading />
<ReelCard loading />
<ReelCard loading />
</div>
);
}
if (reels && reels.length === 0) {
return (
<div className={styles['empty-message-container']}>
<img src="/reel.svg" alt="No Reels" height="120" width="120" />
<p className={styles['empty-message-title']}>
You don&apos;t have any threads
</p>
<p className={styles['empty-message']}>
Create a thread to start a conversation with your communities.
</p>
<CreateReelButton label="CREATE A THREAD" variant="outlined" />
</div>
);
}
return (
<div id="dashboard">
<h2 className="title">Your Threads</h2>
<div className={styles.reels}>
{reels.map((reel) => (
<ReelCard
key={reel.id}
loading={false}
reel={reel}
onClick={() => reelOnClick(reel.id)}
onThreadDeleted={() => threadDeleted(reel)}
/>
))}
</div>
<div className={styles['load-more']} ref={loadMoreRef}>
<Button
theme={ButtonThemes.PRIMARY}
onClick={fetchMoreReels}
loading={loading}
disabled={allReelsLoaded || reels.length < 10}
>
{allReelsLoaded || reels.length < 10
? 'No more threads'
: 'Load More'}
</Button>
</div>
</div>
);
};
export default Dashboard;
index.test.tsx (test file)
import { when, resetAllWhenMocks, verifyAllWhenMocksCalled } from 'jest-when';
import { render, screen, waitFor, userEvent } from '../../../test-utils';
import Dashboard from '.';
import { UserContextValue } from '../../providers/auth';
import useReels from './hooks/useReels';
const userData = {
// dummy user data
};
const reelsData = {
reels: //reels array ,
loading: false,
loadMoreRef: { current: null },
allReelsLoaded: false,
fetchMoreReels: jest.fn(),
threadDeleted: jest.fn(),
};
jest.mock('../../../src/service/user');
jest.mock('../../../src/service/reel');
jest.mock('./hooks/useReels');
jest.mock('../../../src/providers/auth', () => ({
useCurrentUser: () =>
({
user: userData,
} as unknown as UserContextValue),
}));
afterEach(() => {
verifyAllWhenMocksCalled();
resetAllWhenMocks();
});
beforeEach(() => {
when(useReels).expectCalledWith().mockReturnValue(reelsData);
});
describe('Dashboard testing', () => {
test(`"Load more" button should be performed on click`, async () => {
render(<Dashboard />);
const loadMoreButton = screen.getByRole('button', {
name: 'Load More',
});
expect(loadMoreButton).toBeVisible();
await userEvent.click(loadMoreButton);
await waitFor(() => {
expect(jest.fn()).toHaveBeenCalled();
});
});
});
When toHaveBeenCalled always returning 0 but it should return 1.
Can someone tell me why this is return always 0 instead of 1?

Related

Unable to use the server side props in Next JS while using the axios as middleware in the React Context

I would like to get the request using the status side props in Next JS, however, I'm receiving an error that status code is not defines which I declared in the context api file.
This is from my client side next js page
function Instructor({ courses }) {
const router = useRouter();
const { Meta } = Card;
return (
<div className="overflow-hidden">
<InstructorRoute>
<h1 className="jumbotron square">This is Instructor Dashboard</h1>
<div class="row">
{courses &&
courses.map((course, index) => {
return (
<div class="col-sm-4 scaling">
<Badge.Ribbon
text={course.published ? "Published" : "Draft"}
color={course.published ? "green" : "gold"}
>
<div class="card">
<div class="card-body">
<Link href={`/instructor/course/view/${course.slug}`}>
<a className="card-title text-black fw-bold">
<ReactMarkdown> {course.name}</ReactMarkdown>
</a>
</Link>
<p class="card-text text-truncate">
{course.description}
</p>
<img
style={{ cursor: "pointer" }}
onClick={(e) => {
e.preventDefault();
router.push(
`/instructor/course/view/${course.slug}`
);
}}
class="card-img-bottom "
height="200px"
src={
course.image ? course.image.Location : "/course.png"
}
alt="Card image cap"
/>
<hr className="fullWidth" />
<p class="mt-2 card-text">
<p class=" fs-6 badge bg-warning">
{course.lessons.length} Lessons
</p>
</p>
</div>
</div>
</Badge.Ribbon>
</div>
);
})}
</div>
</InstructorRoute>
</div>
);
}
export async function getStaticProps(context) {
const { data } = await axios.get("/api/instructor-courses");
// setCourses(data);
console.log(data);
return {
props: {
courses: data,
},
};
}
export default Instructor;
This is from my context api page:
function Provider({ children }) {
const router = useRouter();
const [state, dispatch] = useReducer(rootReducer, initialState);
useEffect(() => {
dispatch({
type: "LOGIN",
payload: JSON.parse(window.localStorage.getItem("user")),
});
}, []);
axios.interceptors.response.use(
(response) => {
return response;
},
(err) => {
let res = err.response;
console.log(res);
if (res.status === 401 && res.config && !res.config.__isRetryRequest) {
return new Promise((resolve, reject) => {
axios
.get("api/logout")
.then((data) => {
console.log("401 error > logout");
dispatch({ type: "LOGOUT" });
window.localStorage.removeItem("user");
router.push("/login");
})
.catch((err) => {
console.log("Error", err);
reject(err);
});
});
}
return Promise.reject(err);
}
);
useEffect(() => {
async function getCsrfToken() {
const { data } = await axios.get("/api/csrf-token");
console.log("CSRF", data);
axios.defaults.headers["X-CSRF-Token"] = data.getCsrfToken;
}
getCsrfToken();
}, []);
return (
<Context.Provider value={{ state, dispatch }}>{children}</Context.Provider>
);
}
export { Context, Provider };
This is my app.js page:
function MyApp({ Component, pageProps }) {
return (
<Provider>
<Nav></Nav>
<ToastContainer
theme="colored"
position="top-center"
limit={3}
autoClose={1500}
/>
<Component {...pageProps}></Component>
</Provider>
);
}
This is the error that I'm receiving. However, the code works if I don't use server side rendering or static side rendering
Server Error
TypeError: Cannot read property 'status' of undefined
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Source
context\index.js (39:14) # eval
37 | let res = err.response;
38 | console.log(res);
> 39 | if (res.status === 401 && res.config && !res.config.__isRetryRequest) {
| ^
40 | return new Promise((resolve, reject) => {
41 | axios
42 | .get("api/logout")

Builds a chat app in React, using axios and firestore

Please I would be happy if anyone would help me
I have a problem, I can not use the server-side functions, I call the functions with axios, and execute it in react hooks.
I actually build chat, which is why I use react hook, because I want messages to be updated all the time.
I also use firestore. There I save the messages, and receive them through the server side function.
It's a component of the chat - it's causing me problems, I do not understand why.
The server side functions work great, I tested them in postman, and they worked. The problem is that I can't run them in a function component. I do not know what I'm doing wrong.
The error I get here is in the line chat.users.length> 0?, When I make this comparison I get that chat.users is undefined, but I do not understand why because I initialize it at first, using a server side function , Which gives the necessary information
I'm very confused, and I'm new here on the site, I'm trying to figure out why it has not worked for two whole days
I think I might be confused by syntax, for example using an unnecessary dispatch inside component of the chat
i got this error:
enter image description here
component of the chat
import React, { useEffect, useState } from 'react';
import './style.css';
import { useDispatch, useSelector } from 'react-redux';
import { getRealtimeUsers, updateMessage, getRealtimeConversations } from '../../redux/actions/chatActions';
import { Fragment } from 'react';
const User = (props) => {
const { chat, onClick } = props;
return (
<div onClick={() => onClick(chat)} className="displayName">
<div className="displayPic">
<img src="https://i.pinimg.com/originals/be/ac/96/beac96b8e13d2198fd4bb1d5ef56cdcf.jpg" alt="" />
</div>
<div style={{ display: 'flex', flex: 1, justifyContent: 'space-between', margin: '0 10px' }}>
<span style={{ fontWeight: 500 }}>{chat.firstName} {chat.lastName}</span>
<span className={chat.isOnline ? `onlineStatus` : `onlineStatus off`}></span>
</div>
</div>
);
}
const HomePage = (props) => {
const dispatch = useDispatch();
const user = useSelector(state => state.user.credentials);
const chat = useSelector(state => state.chat);
const [chatStarted, setChatStarted] = useState(false);
const [chatUser, setChatUser] = useState('');
const [message, setMessage] = useState('');
const [userUid, setUserUid] = useState(null);
let unsubscribe;
useEffect(() => {
//unsubscribe = dispatch(getRealtimeUsers(user.handle))
dispatch(getRealtimeUsers());
}, []);
//console.log(user);
//componentWillUnmount
useEffect(() => {
return () => {
//cleanup
//unsubscribe.then(f => f()).catch(error => console.log(error));
unsubscribe.then(f => f()).catch(error => console.log(error));
}
}, []);
//function
const initChat = (chat) => {
setChatStarted(true)
setChatUser(`${chat.firstName} ${chat.lastName}`)
setUserUid(chat.handle);
console.log(chat);
dispatch(getRealtimeConversations({ uid_1: user.handle, uid_2: chat.handle }));
}
const submitMessage = (e) => {
const msgObj = {
user_uid_1: user.handle,
user_uid_2: userUid,
message
}
if (message !== "") {
dispatch(updateMessage(msgObj))
.then(() => {
setMessage('')
});
}
//console.log(msgObj);
}
return (
<Fragment>
<section className="container">
<div className="listOfUsers">
{console.log(chat)}
{
//chat.users != undefined
chat.users.length > 0 ?
chat.users.map(user => {
return (
<User
onClick={initChat}
key={user.handle}
user={user}
/>
);
})
: null
}
</div>
<div className="chatArea">
<div className="chatHeader">
{
chatStarted ? chatUser : ''
}
</div>
<div className="messageSections">
{
chatStarted ?
chat.conversations.map(con =>
<div style={{ textAlign: con.user_uid_1 == user.handle ? 'right' : 'left' }}>
<p className="messageStyle" >{con.message}</p>
</div>)
: null
}
</div>
{
chatStarted ?
<div className="chatControls">
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Write Message"
/>
<button onClick={submitMessage}>Send</button>
</div> : null
}
</div>
</section>
</Fragment>
);
}
export default HomePage;
This is the axios:
app.get('/realtimeUsers', FBAuth, getRealtimeUsers );
app.post('/updateMessage', FBAuth, updateMessage);
app.get('/realtimeConversations', FBAuth, getRealtimeConversations);
And this is the server side functions - They work 100% - I checked them many times and they worked.:
const { db } = require('../util/admin');
exports.getRealtimeUsers = (req, res) => {
db.collection("users")
.onSnapshot((querySnapshot) => {
const users = [];
querySnapshot.forEach(function (doc) {
if (doc.data().handle != req.user.handle) {
users.push(doc.data());
}
});
return res.json(users);
});
}
exports.updateMessage = (req, res) => {
db.collection('conversations')
.add({
...req.body,
isView: false,
createdAt: new Date()
})
.then(() => {
return res.json({ message: "Conversations added successfully" });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
}
exports.getRealtimeConversations = (req, res) => {
console.log(JSON.stringify("testing"));
console.log(JSON.stringify(req.query));
console.log(JSON.parse(req.query.user));
console.log(JSON.parse(req.query.user).uid_1);
console.log(JSON.parse(req.query.user).uid_2);
db.collection('conversations')
.where('user_uid_1', 'in', [JSON.parse(req.query.user).uid_1, JSON.parse(req.query.user).uid_2])
.orderBy('createdAt', 'asc')
.onSnapshot((querySnapshot) => {
const conversations = [];
querySnapshot.forEach(doc => {
console.log(JSON.stringify(doc));
if (
(doc.data().user_uid_1 == JSON.parse(req.query.user).uid_1 && doc.data().user_uid_2 == JSON.parse(req.query.user).uid_2)
||
(doc.data().user_uid_1 == JSON.parse(req.query.user).uid_2 && doc.data().user_uid_2 == JSON.parse(req.query.user).uid_1)
) {
conversations.push(doc.data())
}
});
console.log(conversations);
return res.json(conversations);
})
//return res.json([]);
}
this is the actions that used in the client side, here i call to the axios:
import { userConstants } from "../types";
import axios from 'axios';
export const getRealtimeUsers = () => (dispatch) => {
dispatch({ type: `${userConstants.GET_REALTIME_USERS}_REQUEST` });
axios
.get('/realtimeUsers')
.then((res) => {
console.log(res);
dispatch({
type: `${userConstants.GET_REALTIME_USERS}_SUCCESS`,
payload: res.data
});
})
.catch((err) => console.log(err))
}
export const updateMessage = (msgObj) => (dispatch) => {
axios.post('/updateMessage', msgObj)
.then(() => { })
.catch((err) => console.log(err));
}
export const getRealtimeConversations = (user) => (dispatch) => {
//user = { uid_1: "from visualcode", uid_2: "userUid" };
console.log(JSON.stringify(user));
axios.get('/realtimeConversations',
{
params: {
user: JSON.stringify(user)
//uid_1:JSON.stringify("user.handle"),
//uid_2:JSON.stringify("userUid")
}
}
)
.then((res) => {
dispatch({
type: userConstants.GET_REALTIME_MESSAGES,
payload: res.data
});
})
.catch((err) => console.log(err))
}
I am not able to understand your whole code flow, i.e., how the chat.users will be populated before initChat is called.
But still, for your problem, you should always put a check for undefined values while iterating through an array.
<div className="listOfUsers">
{console.log(chat)}
{
//chat.users != undefined
chat && chat.users && chat.users.length > 0 &&
chat.users.map(user => {
return (
<User
onClick={initChat}
key={user.handle}
user={user}
/>
);
})
}
</div>

_id is missing after doing actions

i'm currently creating my first MERN App, and everything is going well, until something happened, and i'm going my try to explain because i need help !
What i'm doing is a facebook clone, where you can post something, you can delete your post and you can update your post, the logic is simple, i call dispatch to pass the data to the actions, the actions pass the data to the backend, and the backend return something to me and it saves in my store, because i'm using redux
The problem is that, when i have 2 post, and i want to delete a post, or maybe i want to edit it, the other post dissapears, it's like it loses its id and then loses the information, then i can't do anything but reaload the page, and it happens always
this is how it looks like, everything fine
Then, after trying to edit a post, the second one lost its information, and in the console, it says that Warning: Each child in a list should have a unique "key" prop, and i already gave each post the key={_id}, but the post lost it and i don't know how
Here's the code
Posts.js
import React, { useState } from "react";
import "./Posts.css";
import moment from "moment";
// Icons
import { BiDotsVertical, BiLike } from "react-icons/bi";
import { MdDeleteSweep } from "react-icons/md";
import { AiFillLike } from "react-icons/ai";
import { GrClose } from "react-icons/gr";
// Calling actions
import { deletePost, } from "../actions/posts.js";
// Gettin The Data From Redux
import { useSelector, useDispatch } from "react-redux";
const Posts = ({ setCurrentId }) => {
const [animation, setAnimation] = useState(false);
const [modal, setModal] = useState(false);
const [modalPost, setModalPost] = useState({});
// Getting The Posts
const posts = useSelector(state => state.posts);
const dispatch = useDispatch();
// Showing And Hiding Modal Window
const ModalWindow = post => {
setModalPost(post);
setModal(true);
};
// Liking the post
// const Like = id => {
// dispatch(giveLike(id));
// setAnimation(!animation);
// };
if (!posts.length) {
return <div>Loading</div>;
} else {
return (
<div className="Posts">
{/* // Modal window for better look to the post */}
{/* {modal && (
<div className="modalWindow">
<div className="container">
<div className="container-image">
<img src={modalPost.image} alt="" />
</div>
<div className="information">
<div className="container-information">
<div className="data-header">
<h2>
User <br />{" "}
<span style={{ fontWeight: "400" }}>
{moment(modalPost.createdAt).fromNow()}
</span>
</h2>
<span className="data-icon" onClick={() => setModal(false)}>
<GrClose />
</span>
</div>
<div className="message">
<h2>{modalPost.title}</h2>
<p>{modalPost.message}</p>
</div>
</div>
</div>
</div>
</div>
)} */}
{/* */}
{posts.map(post => {
const { _id, title, message, image, createdAt, likes } = post;
return (
<div className="Posts-container" key={_id}>
<div className="Fit">
<div className="Fit-stuff">
<h2 className="Fit-stuff_title">
User <br />{" "}
<span style={{ fontWeight: "400" }}>
{moment(createdAt).fromNow()}
</span>
</h2>
<a
className="Fit-stuff_edit"
href="#form"
onClick={() => setCurrentId(_id)}
>
<BiDotsVertical />
</a>
</div>
<div className="Fit-data">
<h2 className="Fit-data_title">{title}</h2>
<p className="Fit-data_message">{message}</p>
{image ? (
<div className="Fit-img">
<img
onClick={() => ModalWindow(post)}
src={image}
alt=""
/>
</div>
) : (
<div></div>
)}
</div>
<div className="Fit-shit">
<span>
{animation ? (
<AiFillLike className="fullLightBlue" />
) : (
<BiLike />
)}
{likes}
</span>
<span onClick={() => dispatch(deletePost(_id))}>
<MdDeleteSweep />
</span>
</div>
</div>
</div>
);
})}
</div>
);
}
};
export default Posts;
The form where i call update and create Post
import React, { useState, useEffect } from "react";
import Filebase from "react-file-base64";
// For the actions
import { useDispatch, useSelector } from "react-redux";
import { createPost, updatePost } from "../actions/posts.js";
import {
Wrapper,
FormContainer,
Data,
DataInput,
SecondDataInput,
FormContainerImg,
FormContainerButtons,
Buttons
} from "./FormStyled.js";
const Form = ({ currentId, setCurrentId }) => {
const [formData, setFormData] = useState({
title: "",
message: "",
image: ""
});
const specificPost = useSelector(state =>
currentId ? state.posts.find(p => p._id === currentId) : null
);
// Sending The Data And Editing The data
const dispatch = useDispatch();
useEffect(() => {
if (specificPost) setFormData(specificPost);
}, [specificPost]);
// Clear Inputs
const clear = () => {
setCurrentId(0);
setFormData({ title: "", message: "", image: "" });
};
const handleSubmit = async e => {
e.preventDefault();
if (currentId === 0) {
dispatch(createPost(formData));
clear();
} else {
dispatch(updatePost(currentId, formData));
clear();
}
};
return (
<Wrapper>
<FormContainer onSubmit={handleSubmit}>
<Data>
<DataInput
name="title"
maxLength="50"
placeholder="Title"
type="text"
value={formData.title}
onChange={e => setFormData({ ...formData, title: e.target.value })}
/>
<SecondDataInput
name="message"
placeholder="Message"
maxLength="300"
value={formData.message}
required
onChange={e =>
setFormData({ ...formData, message: e.target.value })
}
/>
<FormContainerImg>
<Filebase
required
type="file"
multiple={false}
onDone={({ base64 }) =>
setFormData({ ...formData, image: base64 })
}
/>
</FormContainerImg>
<FormContainerButtons>
<Buttons type="submit" create>
{specificPost ? "Edit" : "Create"}
</Buttons>
<Buttons onClick={clear} clear>
Clear
</Buttons>
</FormContainerButtons>
</Data>
</FormContainer>
</Wrapper>
);
};
export default Form;
My actions
import {
GETPOSTS,
CREATEPOST,
DELETEPOST,
UPDATEPOST,
LIKEPOST
} from "../actionTypes/posts.js";
import * as api from "../api/posts.js";
export const getPosts = () => async dispatch => {
try {
const { data } = await api.getPosts();
dispatch({ type: GETPOSTS, payload: data });
} catch (error) {
console.log(error);
}
};
export const createPost = newPost => async dispatch => {
try {
const { data } = await api.createPost(newPost);
dispatch({ type: CREATEPOST, payload: data });
} catch (error) {
console.log(error);
}
};
export const updatePost = (id, updatePost) => async dispatch => {
try {
const { data } = await api.updatePost(id, updatePost);
dispatch({ type: UPDATEPOST, payload: data });
} catch (error) {
console.log(error);
}
};
export const deletePost = id => async dispatch => {
try {
await api.deletePost(id);
dispatch({ type: DELETEPOST, payload: id });
} catch (error) {
console.log(error);
}
};
Redux Part
import {
GETPOSTS,
CREATEPOST,
DELETEPOST,
UPDATEPOST,
LIKEPOST
} from "../actionTypes/posts.js";
const postData = (posts = [], action) => {
switch (action.type) {
case GETPOSTS:
return action.payload;
case CREATEPOST:
return [...posts, action.payload];
case UPDATEPOST:
return posts.map(post =>
action.payload._id === post._id ? action.payload : posts
);
case DELETEPOST:
return posts.filter(post => post._id !== action.payload);
default:
return posts;
}
};
export default postData;
My controllers in the backend
import mongoose from "mongoose";
import infoPost from "../models/posts.js";
// Getting All The Posts
export const getPosts = async (req, res) => {
try {
const Posts = await infoPost.find();
res.status(200).json(Posts);
} catch (error) {
res.status(404).json({ message: error.message });
console.log(error);
}
};
// Creating A Post
export const createPost = async (req, res) => {
const { title, message, image } = req.body;
const newPost = new infoPost({ title, message, image });
try {
await newPost.save();
res.status(201).json(newPost);
} catch (error) {
res.status(409).json({ message: error.message });
console.log(error);
}
};
// Update A Post
export const updatePost = async (req, res) => {
const { id } = req.params;
const { title, message, image } = req.body;
if (!mongoose.Types.ObjectId.isValid(id))
return res.status(404).send(`No Post With Id Of ${id}`);
const updatedPost = { title, message, image, _id: id };
await infoPost.findByIdAndUpdate(id, updatedPost, { new: true });
res.json(updatedPost);
};
// Deleting A Post
export const deletePost = async (req, res) => {
const { id } = req.params;
if (!mongoose.Types.ObjectId.isValid(id))
return res
.status(404)
.send(`We Couldnt Found The Post With Id Of ${id} To Delete`);
await infoPost.findByIdAndRemove(id);
res.json(`Post With Id Of ${id} Deleted Succesfully`);
};
// Liking A Post
export const likePost = async (req, res) => {
const { id } = req.params;
if (!mongoose.Types.ObjectId.isValid(id))
return res.status(404).send(`No post with id: ${id}`);
const post = await infoPost.findById(id);
const updatedPost = await infoPost.findByIdAndUpdate(
id,
{ likeCount: post.likeCount + 1 },
{ new: true }
);
res.json(updatedPost);
};
Even though i've been trying to solve this problem for nearly 3.5 hours, i think that the problem might be in my Posts.js part, if you can help me, you're the greatest !

Delete an item with ReactJS

I'm trying to delete an item after the user clicks on the Delete button. Through the handleDelete function, I am passing an id (idBooks) via axios when the user clicks a book. How do I withdraw it from the click?
Below you will find the React code and then the backend side code in node js.
Frontend
(class extends React.Component {
handleDelete = (e) => {
e.preventDefault();
const { book } = this.props;
axios.delete("http://localhost:8081/delete", book.idBooks )
.then(res => {
console.log(res.data);
}).catch(err => {
console.warn(err.warn);
});
};
render() {
const { book, classes } = this.props;
const token = localStorage.getItem('token');
return(
<Paper className= { classes.marginTopBottom }>
<h2 className={ classes.title }>
{ book.title }
</h2><hr />
<div className= { classes.scrollingDiv }>
<p>
{ book.plot }
</p>
</div>
<hr/>
<div className={ classes.pStyle }>
<p>Publish date:<br /> { new Date(book.publish_date).toLocaleDateString() }</p>
<p>Author:<br /> { book.author }
</p>
<p>Genre:<br /> { book.genre }</p>
</div>
<div>
{ token && (
<Button className={ classes.delete } size="small" onClick={this.handleDelete} type="button" variant="contained" color="primary"
component= {Link} to="delete">
Delete
<DeleteIcon className={ classes.rightIcon } />
</Button>
)}
</div>
</Paper>
)
}
});
Backend
const deleteBook = (req, res) => {
const connection = mysql.createConnection(connectionProperties);
connection.connect();
const query = `DELETE FROM Books WHERE idBooks = ${ req.body.idBooks }`;
connection.query(query, (err, res) => {
if (err) {
res.status(500).send(err);
} else {
res.status(200).send('Book deleted correctly.');
}
});
};
I'd add a prop onDeleteCallback, and on successful delete call that function with deleted book id. In parent component (with all the books are listed) update the state with filtered out books.
I guess passing the parameter might help you fix this issue.
On the delete Button add a parameter to the onClick={()=>this.handleDelete(e,book.idBooks)}
Change the handleDelete function a bit as below
handleDelete = (e,idBooks) => {
e.preventDefault();
axios.delete("http://localhost:8081/delete", idBooks )
.then(res => {
console.log(res.data);
}).catch(err => {
console.warn(err.warn);
});
};

After I submit my form the component does not update to render the new information

I have a component with a forum (redux-form) that adds a food to the data. After I click "ADD" the information I add in the form is added to the database, however, the component that renders the list of food does not update automatically unless I manually refresh the page. Then the new food is rendered in the component.
Not sure why this is not rendering the new information automatically
Stock.js (where the food list is rendered)
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchFoodList, addToPot, deleteFood } from '../actions';
import Header from './header';
import Pot from './pot';
import AddFood from './add_food';
import RecipeList from './recipe_list';
import classes from "../index.css";
import { Row, Col } from 'react-flexbox-grid';
class FoodList extends Component {
componentDidMount () {
this.props.fetchFoodList();
}
addIngredientToPot = (ev) => {
const val = ev.target.dataset.value;
const newPot = [ ...this.props.pot,
val
];
this.props.addToPot(newPot)
}
onDeleteClick = (ev) =>{
// const {id} = this.props.match.params;
const val = ev.target.dataset.value;
this.props.deleteFood(val);
}
displayFoodList() {
return _.map(this.props.foods, food => {
return (
<li key={food._id} className={classes.tablerow}>
<div data-value={food.name}
onClick={this.addIngredientToPot.bind(this)}
className={classes.col1}>{food.name}
</div>
<div className={classes.col2}>{food.type}</div>
<div className={classes.col3}>
<button data-value={food._id}
onClick={this.onDeleteClick.bind(this)}
className={classes.throwoutbutton}
> Throw Out
</button>
</div>
</li>
);
});
}
render () {
// console.log(this.props.foods);
// console.log(this.props.pot)
return (
<div className={classes.stockpilecontainer}>
<Header />
<Row>
<Col lg >
<h2>StockPile</h2>
<ul className={classes.responsivetable}>
<li className={classes.tableheader}>
<div className={classes.col1}>Name</div>
<div className={classes.col2}>Type</div>
<div className={classes.col3}>Throw Out</div>
</li>
{this.displayFoodList()}
</ul>
<div>
<AddFood/>
</div>
</Col>
<Col/>
<Pot/>
<Col/>
</Row>
<Row>
<Col xs={12}>
<Row center="xs">
<RecipeList xs={6} />
</Row>
</Col>
</Row>
</div>
);
};
}
function mapStateToProps(state) {
return {
foods: state.foods,
pot: state.pot.pot,
};
}
export default connect (mapStateToProps, { fetchFoodList, addToPot, deleteFood })(FoodList);
Newfood.js (component with form to add a new food item)
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { addFood } from '../actions';
import classes from "../index.css";
import { Redirect } from 'react-router-dom';
const renderField= field => {
const { input, type } = field;
return (
<div className={classes.addfoodinput}>
<label className={classes.addfoodlabel}>{field.label}</label>
<input {...input} type={type} className="form-control" />
</div>
)
}
class AddFood extends Component {
onSubmit(values) {
this.props.addFood(values);
}
render () {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))} className={classes.addfoodform}>
<Field
label="Food "
name="name"
component={renderField}
/>
<Field
label="Type "
name="type"
component={renderField}
/>
<button type="submit" className={classes.addfoodbutton} >ADD</button>
<button className={classes.addfoodbutton}>Cancel</button>
{/* <Redirect to={'/stockpile'} /> */}
</form>
)
}
};
AddFood = reduxForm({
form: 'NewFoodForm',
fields: ['name', 'type'],
})(AddFood);
export default connect(null, {addFood})(AddFood)
Reducer for the food list:
import _ from 'lodash';
import { FETCH_FOODLIST } from '../actions/types';
export default function(state = {}, action) {
switch (action.type) {
case FETCH_FOODLIST:
return _.mapKeys(action.payload.data, '_id');
default:
return state;
}
}
Action creators for adding new food and get the food list:
// Action to get the food list:
export const fetchFoodList = () => dispatch => {
Axios
.get(`${ROOT_URL}/myfoodlist`, {
headers: { auth: localStorage.getItem('token') }
})
.then( response =>
dispatch({
type: FETCH_FOODLIST,
payload: response
}));
};
// Add a food to the DB:
export function addFood (values, history) {
return function (dispatch) {
Axios.post(`${ROOT_URL}/myfoodlist/foods`, values)
.then( response => {
dispatch ({
type: ADD_NEW_FOOD,
payload: response
});
// history.push('/');
history.pushState(null, '/stockpile');
})
.catch( () => {
});
};
}

Resources