How can I conver the MUI component to html string, so all the styles we be included as well?
My main goal is to create a dynamic template form and to save as PDF file on server side.
i'm trying to send the component as html string, in order to generate PDF file. There is only one problem, all mui styles got lost in pdf file
This is what I'm doing now:
client:
const formTemplate = (user)=>{
return (
<Grid container>
<Grid item lg={4} xs={12}>
<TextField fullWidth
defaultValue={user.firstName} label={"First Name"} />
</Grid>
<Grid item lg={4} xs={12}>
<TextField fullWidth
defaultValue={user.familyName} label={"FamilyName"} />
</Grid>
</Grid>)
}
function saveForm(user){
const formTamplate = ReactDOMServer.renderToString(formTemplate(user))
try {
const response = await axios.post(`${url}/consent/sign`, {
email: user.email,
formTamplate: formTamplate
});
dispatch({
type: ActionType.setEnvelopeInfo,
payload: {
envelope: response.data.envelope,
},
});
} catch (err) {
console.log(err);
}
}
server:
router.post("/sign", async (req, res) => {
const { email, formTamplate } = req.body;
const formTemplatePath = `${signed_docs}\\${name}.pdf`;
let options = { format: "A4" };
let file = { content: formTamplate};
try {
await html_to_pdf.generatePdf(file, options).then((pdfBuffer: any) => {
console.log({ pdfBuffer: pdfBuffer });
console.log(typeof pdfBuffer);
fs.writeFile(formTemplatePath, pdfBuffer, (err) => {
if (err) {
console.log({ files_err: err });
return;
}
});
console.log("PDF Buffer:-", pdfBuffer);
});
} catch (err) {
console.log({ pdf_err: err });
}
import { useRef } from 'react';
import './App.css';
import { Button, Box, Link, Typography, Grid } from '#mui/material'
import ReactToPrint from 'react-to-print';
function App() {
const reference = useRef();
return (
<>
<Button onClick={()=>{
console.log(reference.current)
}}>ref</Button>
<Box sx={{textAlign:'center'}}>
<ReactToPrint
trigger={() => <Link>Print</Link>}
content={() => reference.current!}/>
{/* Place the component here and set the reference */}
<Box ref={reference} sx={{textAlign:"center"}}>
<br/>
<Typography variant='h4'>Booking details</Typography>
<Grid container direction="column" spacing={1}>
<Grid container item direction="row" justifyContent='space-around'>
<Typography variant="body1" color="initial">VAT</Typography>
<Typography variant="body1" color="initial">$XXX</Typography>
</Grid>
<Grid container item direction="row" justifyContent='space-around'>
<Typography variant="body1" color="initial">VAT</Typography>
<Typography variant="body1" color="initial">$XXX</Typography>
</Grid>
</Grid>
</Box>
</Box>
</>
);
}
export default App;
console.log(reference.current) logs this output
click here to see
useRef hook can be used and the reference has that everything converted to html.
suggestion: react-to-print can be used to directly convert mui components easily, have a look into it. Hope this helps!
Related
I am currently busy working on a personal project with a dashboard using NextJS, which I believe allows me to learn NextJS and the fundementals of typescript. I am trying to work out how to set a welcome message if a response sent back from the server is set to True, I did manage to code this in, but I got cannot set headers after they are sent to the client.
My entire code file for the dashboard is the following snippet:
import styles from "./index.module.css";
import { type NextPage } from "next";
import Head from "next/head";
import Link from "next/link";
import Container from 'react-bootstrap/Container';
import Nav from 'react-bootstrap/Nav';
import Navbar from 'react-bootstrap/Navbar';
import NavDropdown from 'react-bootstrap/NavDropdown';
import { signIn, signOut, useSession } from "next-auth/react";
import Image from 'react-bootstrap/Image'
import Router from "next/router";
import { useEffect, useState } from "react";
import { library } from '#fortawesome/fontawesome-svg-core'
import { faHouse, fas, faServer } from '#fortawesome/free-solid-svg-icons'
import "#fortawesome/fontawesome-svg-core/styles.css";
import { Offline, Online } from "react-detect-offline";
import { config } from "#fortawesome/fontawesome-svg-core";
// Tell Font Awesome to skip adding the CSS automatically
// since it's already imported above
config.autoAddCss = false;
{/* The following line can be included in your src/index.js or _App.js file*/}
import 'bootstrap/dist/css/bootstrap.min.css';
library.add(fas, faServer, faHouse)
// import the icons you need
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
const Protected: NextPage = () => {
const { status, data } = useSession()
const { data: sessionData2 } = useSession();
useEffect(() => {
if (status === "unauthenticated") Router.replace("/auth/signin");
}, [status]);
// const isBreakpoint = useMediaQuery(768)
// return (
// <div>
// { isBreakpoint ? (
// <div>
// <HamburgerMenu />
// </div>
// ) : (
// <div>
// <FullMenu />
// </div>
// )
if (status === "authenticated")
return (
<><Navbar bg="dark" expand="lg" variant="dark" className="justify-content-end flex-grow-1 pe-3">
<Container>
<Navbar.Brand href="#home" style={{ fontSize: '25px' }}><strong>Litika.</strong> </Navbar.Brand>
{/* <Navbar.Toggle aria-controls="basic-navbar-nav" /> */}
<Navbar.Collapse id="basic-navbar-nav" className={styles.rightNavbar}>
<Nav className={styles.rightNavbar}>
<Nav.Link href="#home" className={styles.body}><FontAwesomeIcon icon={faHouse} /></Nav.Link>
<Nav.Link href="#link" className={styles.body}>Link</Nav.Link>
<NavDropdown className={styles.body} title={<Image
src={sessionData2 ? `https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_640.png` : 'https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_640.png'}
roundedCircle
style={{ width: '30px' }} />}
id="basic-nav-dropdown">
<NavDropdown.Item href="#action/3.1" onClick={sessionData2 ? () => void signOut() : () => void signIn()}>
{sessionData2 ? "Sign out" : "Sign in"}
</NavDropdown.Item>
<NavDropdown.Item href="#action/3.2">
Another action
</NavDropdown.Item>
<NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item href="#action/3.4">
Separated link
</NavDropdown.Item>
</NavDropdown>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
<div className={styles.connectivityStatus}>
<br></br>
<div className={styles.connectivityStatusBox}>
<Online>🟢 You are connected to the internet.</Online>
<Offline>🔴 You are not connected to the internet.</Offline>
</div>
<WelcomeContainer />
</div>
<main className={styles.main}>
<h1 className={styles.title}>
Litika. </h1>
<div className={styles.container}>
<div className={styles.cardRow}>
<div className={styles.card}>
<h1 className={styles.cardTitle}><FontAwesomeIcon icon={faServer} /> HELLO!</h1>
<p className={styles.cardText}>How are you? I am under the water.</p>
</div>
</div>
</div>
</main>
<main className={styles.main2}>
<div className={styles.container}>
</div>
</main>
</>
);
return (
<>
<main className={styles.main}>
<div className={styles.container}>
<h1 className={styles.title}>
Loading the Dashboard.
</h1>
</div>
</main>
<main className={styles.main2}>
<div className={styles.container}>
<h1 className={styles.title}>
</h1>
</div>
</main>
</>
);
};
export default Protected;
const WelcomeContainer: React.FC = () => {
const { data: sessionData } = useSession();
var [welcomeMessage, setWelcomeMessage] = useState(null);
const payload = JSON.stringify({
email: sessionData.user?.email,
});
fetch('/api/userService/isuserwelcome', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: payload
})
.then(response => response.json())
.then(data => {
console.log(data);
if (data === true) {
setWelcomeMessage(
<div className={styles.welcomeContainer}>
<h1>Welcome to the Los pollos hermanos family.</h1>
</div>
);
}
})
.catch(error => {
console.error(error);
});
return (
<>
{welcomeMessage}
</>
);
};
The code for the IsUserWelcome API Route is the following:
import type { NextApiRequest, NextApiResponse } from 'next';
import { PrismaClient } from '#prisma/client';
const prisma = new PrismaClient();
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const { email } = req.body;
const cliresponse = await prisma.user.findFirst({
where: {
email,
},
});
console.log(cliresponse.newUser)
if (cliresponse.newUser == true) {
res.status(200).json(cliresponse.newUser)
}
res.status(200).json({ cliresponse });
} catch (err) {
console.log(err)
res.status(500).json({ error: err});
}
}
I have identified the issue to be coming from the WelcomeContainer Functional Component and do understand that NextJS & React are dynamic, which will lead to updates through the DOM. However, I haven't really tried anything yet to fix this issue because nothing from doing a simple Google search could lead me to fix this issue, so any guidance & help will be appreciated!
The parts that are different in this question is that it pertains to react and NextJS in general, not express.
You are sending a response twice, after a response is send you should return from the function.
import type { NextApiRequest, NextApiResponse } from 'next';
import { PrismaClient } from '#prisma/client';
const prisma = new PrismaClient();
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const { email } = req.body;
const cliresponse = await prisma.user.findFirst({
where: {
email,
},
});
console.log(cliresponse.newUser)
if (cliresponse.newUser == true) {
res.status(200).json(cliresponse.newUser)
return; // you need to return here so the code below doesn't get executed
}
res.status(200).json({ cliresponse });
} catch (err) {
console.log(err)
res.status(500).json({ error: err});
}
}
I am tring to create a video streaming app, and I got an error logging: "Invalid hook call" when i set "{ ready, tracks } = useMicrophoneAndCameraTracks();" and client = useClient();
I tried not calling the hook useMicrophoneAndCameraTracks and useClient, the error wasn't there but the video won't stream. Here is my code
Settings.js
import { createClient, createMicrophoneAndCameraTracks } from "agora-rtc-react";
const appId = "d13f5075f00d417092285bc8a52f17f2";
const token = "007eJxTYOjZ/X7lClbPsGXOv7vW/egUYDx+N+WSl9Bzddlf2fyLD5orMKQYGqeZGpibphkYpJgYmhtYGhlZmCYlWySaGqUZmqcZKT+bk9wQyMiwSdaKkZEBAkF8FobcxMw8BgYAbJIfFw=="
export const config = { mode: "rtc", codec: "vp8", appId: appId, token: token };
export const useClient = createClient(config);
export const useMicrophoneAndCameraTracks = createMicrophoneAndCameraTracks();
export const channelName = "main";
VideoCall.js
import { useState, useEffect } from "react";
import {
config,
useClient,
useMicrophoneAndCameraTracks,
channelName,
} from "./settings.js";
import { Grid } from "#material-ui/core";
import Video from "./Video";
import Controls from "./Controls";
export default function VideoCall(props) {
const { setInCall } = props;
const [users, setUsers] = useState([]);
const [start, setStart] = useState(false);
const client = useClient();
const { ready, tracks } = useMicrophoneAndCameraTracks();
useEffect(() => {
let init = async (name) => {
client.on("user-published", async (user, mediaType) => {
await client.subscribe(user, mediaType);
if (mediaType === "video") {
setUsers((prevUsers) => {
return [...prevUsers, user];
});
}
if (mediaType === "audio") {
user.audioTrack.play();
}
});
client.on("user-unpublished", (user, mediaType) => {
if (mediaType === "audio") {
if (user.audioTrack) user.audioTrack.stop();
}
if (mediaType === "video") {
setUsers((prevUsers) => {
return prevUsers.filter((User) => User.uid !== user.uid);
});
}
});
client.on("user-left", (user) => {
setUsers((prevUsers) => {
return prevUsers.filter((User) => User.uid !== user.uid);
});
});
try {
await client.join(config.appId, name, config.token, null);
} catch (error) {
console.log("error");
}
if (tracks) await client.publish([tracks[0], tracks[1]]);
setStart(true);
};
if (ready && tracks) {
try {
init(channelName);
} catch (error) {
console.log(error);
}
}
}, [channelName, client, ready, tracks]);
return (
<Grid container direction="column" style={{ height: "100%" }}>
<Grid item style={{ height: "5%" }}>
{ready && tracks && (
<Controls tracks={tracks} setStart={setStart} setInCall={setInCall} />
)}
</Grid>
<Grid item style={{ height: "95%" }}>
{start && tracks && <Video tracks={tracks} users={users} />}
</Grid>
</Grid>
);
}
App.js
import { useState } from "react";
import { Button } from "#material-ui/core";
import VideoCall from "./VideoCall";
function App() {
const [inCall, setInCall] = useState(false);
return (
<div className="App" style={{ height: "100%" }}>
{inCall ? (
<VideoCall setInCall={setInCall} />
) : (
<Button
variant="contained"
color="primary"
onClick={() => setInCall(true)}
>
Join Call
</Button>
)}
</div>
);
}
export default App;
Controls.js
import { useState } from 'react';
import { useClient } from './settings';
import { Grid, Button } from '#material-ui/core';
import MicIcon from '#material-ui/icons/Mic';
import MicOffIcon from '#material-ui/icons/MicOff';
import VideocamIcon from '#material-ui/icons/Videocam';
import VideocamOffIcon from '#material-ui/icons/VideocamOff';
import ExitToAppIcon from '#material-ui/icons/ExitToApp';
export default function Controls(props) {
const client = useClient();
const {tracks, setStart, setInCall} = props;
const [trackState, setTrackState] = useState({video: true, audio: true});
const mute = async(type)=> {
if(type === "audio") {
await tracks[0].setEnabled(!trackState.audio);
setTrackState((ps) => {
return {...ps, audio: !ps.audio}
})
}
else if(type === "video") {
await tracks[1].setEnabled(!trackState.video);
setTrackState((ps) => {
return {...ps, video: !ps.video}
})
}
}
const leaveChannel = async() => {
await client.leave();
client.removeAllListeners();
tracks[0].close();
tracks[1].close();
setStart(false);
setInCall(false);
}
return (
<Grid container spacing={2} alignItems='center'>
<Grid item>
<Button variant="contained" color={trackState.audio ? "primary" : "secondary"} onclick={() => mute("audio")}>
{trackState.audio ? <MicIcon/> : <MicOffIcon/>}
</Button>
</Grid>
<Grid item>
<Button variant="contained" color={trackState.video ? "primary" : "secondary"} onclick={() => mute("video")}>
{trackState.video ? <VideocamIcon/> : <VideocamOffIcon/>}
</Button>
</Grid>
<Grid item>
<Button variant="contained" color="default" onclick={() => leaveChannel()}>
Leave <ExitToAppIcon/>
</Button>
</Grid>
</Grid>
)
}
Video.js
import { AgoraVideoPlayer } from "agora-rtc-react";
import { Grid } from "#material-ui/core";
import { useState, useEffect } from "react";
export default function Video(props) {
const { users, tracks } = props;
const [gridSpacing, setGridSpacing] = useState(12);
useEffect(() => {
setGridSpacing(Math.max(Math.floor(12 / (users.length + 1)), 4));
}, [users, tracks]);
return (
<Grid container style={{ height: "100%" }}>
<Grid item xs={gridSpacing}>
<AgoraVideoPlayer
videoTrack={tracks[1]}
style={{ height: "100%", width: "100%" }}
/>
</Grid>
{users.length > 0 &&
users.map((user) => {
if (user.videoTrack) {
return (
<Grid item xs={gridSpacing}>
<AgoraVideoPlayer
videoTrack={user.videoTrack}
key={user.uid}
style={{ height: "100%", width: "100%" }}
/>
</Grid>
);
} else return null;
})}
</Grid>
);
}
Without more detailed code, we can't help you. You probably broke a rule regarding react hooks.
The official docs state the following:
There are three common reasons you might be seeing it:
You might have mismatching versions of React and React DOM.
You might be breaking the Rules of Hooks.
You might have more than one copy of React in the same app.
Read more about it here:
https://reactjs.org/warnings/invalid-hook-call-warning.html
I'm doing a amazon clone with next.js and fakestoreapi products data but Im not able to receive the data to the browser. but they receives to the vscode console. and I'm not even getting any error messages too. my code is as follows.
index.js
import Head from "next/head";
import Header from "../src/Components/Header";
import Banner from "../src/Components/Banner";
import ProductFeed from "../src/Components/ProductFeed";
export default function Home({ products }) {
return (
<div>
<Head>
<title>ShopBag | amazon Clone</title>
</Head>
<Header />
<main className="max-w-screen-2xl bg-gray-100 mx-auto">
{/* banner */}
<Banner />
{/* product Feild */}
<ProductFeed products={products} />
</main>
</div>
);
}
export async function getServerSideProps(context) {
const products = await fetch("https://fakestoreapi.com/products")
.then((res) => res.json())
.then((json) => console.log(json));
return {
props: {
products: products || null,
},
};
}
ProductFeed.js
import React from "react";
import Product from "./Product";
function ProductFeed({ products }) {
return (
<div>
{products?.map(({ id, title, price, description, category, image }) => (
<Product
key={id}
title={title}
price={price}
description={description}
category={category}
image={image}
/>
))}
</div>
);
}
export default ProductFeed;
Product.js
import React from "react";
import Image from "next/image"
function Product({ id, title, price, description, category, image }) {
return (
<div>
<p>{category}</p>
<Image src={image} height={200} width={200} objectfit="contain" alt="product-images" />
</div>
);
}
export default Product;
next.config.js
/** #type {import('next').NextConfig} */
const isProd = process.env.NODE_ENV === "production";
const nextConfig = {
reactStrictMode: true,
Images: {
domains: ["./public", "fakestoreapi.com"],
assetPrefix: isProd ? "/amazon-clone/" : "",
},
};
module.exports = nextConfig;
It's because you log the json in the second then, instead of returning it. You should do :
....
.then(json => json)
Or if you want to log it :
.then(json => {
console.log(json)
return json
})
you can remove then and use await
const response = await fetch("https://fakestoreapi.com/products");
const products = await response.json();
return {
props: {
products: products || null,
},
};
I can just type any username in the url and the same component gets rendered with the logged-in user's posts. Like when I'm logged in as rick, I can see my feed as http://localhost:3000/profile/Rick/posts
but if I type the url with any string or number or anything, it still renders the component. Like http://localhost:3000/profile/xcxcxcxcx/posts, it still shows the same posts as Rick's feed.
I think I have to verify the username.
Here's my code.
UserProfile
import React, { Component } from "react"
import { getCurrentUser } from "../actions/userActions"
import { connect } from "react-redux"
import { Link } from "react-router-dom"
class UserProfile extends Component {
componentDidMount() {
const authToken = localStorage.getItem("authToken")
this.props.dispatch(getCurrentUser(authToken))
}
render() {
const { isIdentifyingToken, username, email } = this.props
return (
<div>
{isIdentifyingToken ? null : (
<div className="card">
<div className="card-content">
<div className="media">
<div className="media-left">
<figure className="image is-48x48">
<img
src="https://bulma.io/images/placeholders/96x96.png"
alt="Placeholder image"
/>
</figure>
</div>
<div className="media-content">
<p className="title is-4">{username}</p>
<p className="subtitle is-6">{email}</p>
</div>
</div>
<Link to={`/profile/${username}/posts`}>My Posts</Link>
<br></br>
</div>
</div>
)}
</div>
)
}
}
const mapStateToProps = state => {
return {
isIdentifyingToken: state.auth.isIdentifyingToken,
username: state.auth.user.username,
email: state.auth.user.email,
id: state.auth.user._id
}
}
export default connect(mapStateToProps)(UserProfile)
UserFeed
import React, { Component } from "react"
import { getUserPosts, getCurrentUser } from "../actions/userActions"
import { connect } from "react-redux"
import Cards from "./Cards"
class UserFeed extends Component {
componentDidMount() {
const authToken = localStorage.getItem("authToken")
if (authToken) {
this.props.dispatch(getCurrentUser(authToken))
if (this.props && this.props.userId) {
this.props.dispatch(getUserPosts(this.props.userId))
} else {
return null
}
}
}
render() {
const { isFetchingUserPosts, userPosts } = this.props
return isFetchingUserPosts ? (
<p>Fetching....</p>
) : (
<div>
{userPosts &&
userPosts.map((post) => {
return <Cards key={post._id} post={post} />
})}
</div>
)
}
}
const mapStateToPros = (state) => {
return {
isFetchingUserPosts: state.userPosts.isFetchingUserPosts,
userPosts: state.userPosts.userPosts,
userId: state.auth.user._id,
}
}
export default connect(mapStateToPros)(UserFeed)
Cards
import React, { Component } from "react"
import { connect } from "react-redux"
import { compose } from "redux"
import { withRouter } from "react-router-dom"
class Cards extends Component {
render() {
const {title, description } = this.props.post
return (
<div className="card">
<div className="card-content">
<div className="media">
<div className="media-left">
<figure className="image is-48x48">
<img
src="https://bulma.io/images/placeholders/96x96.png"
alt="Placeholder image"
/>
</figure>
</div>
<div className="media-content" style={{ border: "1px grey" }}>
<p className="title is-5">{title}</p>
<p className="content">{description}</p>
</div>
</div>
</div>
</div>
)
}
}
const mapStateToProps = state => {
return state
}
export default compose(withRouter, connect(mapStateToProps))(Cards)
I have a backend route which verifies the user like: (it just takes the decoded object out from the token which contains the userId and verifies the user)
identifyUser: async (req, res, next) => {
try {
const userId = req.user.userId
const user = await User.findOne({ _id: userId })
if (!user) {
return res.status(500).json( {error: "No user found "})
}
return res.status(200).json( { user })
} catch(error) {
return next(error)
}
}
I'm not sure how do I verify the username in the params:(
Edit: App.js
render() {
return (
<div>
<Router>
<Switch>
<Route exact path="/" component={LandingPage} />
<Route path="/register" component={RegistrationForm} />
<Route path="/login" component={LoginForm} />
<PrivateRoute path="/feed" component={Feed} />
<PrivateRoute path="/post/new" component={NewForm} />
<PrivateRoute path="/post/edit/:id" component={EditForm} />
<PrivateRoute exact path="/profile/:username" component={UserProfile} />
<PrivateRoute path="/profile/:username/posts" component={UserFeed} />
<Route component={NotFoundPage} />
</Switch>
</Router>
</div>
)
}
UserFeed is rendered with the component prop, so it receives the route props, match, location, and history. The match prop is the one you should be interested in.
this.props.match.params.username
UserFeed, however, needs to react to the match prop updating and refetch the posts. Here I believe I'm correctly assuming the path's username is the same as the user's userId property. (feel free to correct me if assumption is incorrect). This you'll need the componentDidUpdate lifecycle function.
componentDidUpdate(prevProps) {
const { match: { params: { username } } } = this.props;
const { match: { params: { username: prevUsername } } } = prevProps;
if (prevUsername !== username) {
// whatever other logic you need to validate user first
this.props.dispatch(getUserPosts(username));
}
}
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);
});
};