Page crashes(reading properties of undefined) in React.js - reactjs

In my project I'm using redux toolkit and react router v6. I have invoices list with 'View' button and when it's clicked it should open page with description about invoice. Also i have add invoice feature. When invoice added and I click on 'View' button page crashes and the error in console say:
Uncaught TypeError: Cannot read properties of undefined (reading 'invoice_num')
And the same happens if i click on existing item and reload the page. It says the error occured in InvoiceItem.js page.
Now the code. InvoiceItem.js
import React from "react";
import { useParams } from "react-router-dom";
import InvoiceItemDescription from "../Invoice/InvoiceItemDescription";
import { INVOICES_LIST } from "./InvoicesList";
const InvoiceItem = () => {
const params = useParams();
const invoice = INVOICES_LIST.find(
(invoice) => invoice.id === params.invoiceId
);
return (
<InvoiceItemDescription
invoiceNumber={invoice.invoice_num}
status={invoice.status}
order_date={invoice.order_date}
bill_from={invoice.bill_from}
bill_from_address={invoice.bill_from_address}
bill_from_email={invoice.bill_from_email}
bill_from_fax={invoice.bill_from_fax}
bill_from_phone={invoice.bill_from_phone}
bill_to={invoice.bill_to}
bill_to_address={invoice.bill_to_address}
bill_to_email={invoice.bill_to_email}
bill_to_fax={invoice.bill_to_fax}
bill_to_phone={invoice.bill_to_phone}
item_name={invoice.ITEMS.item_name}
unit_costs={invoice.ITEMS.unit_costs}
unit={invoice.ITEMS.unit}
price={invoice.ITEMS.price}
/>
);
};
export default InvoiceItem;
InvoiceItemDescription.js file
import React from "react";
import Wrapper from "../../UI/Wrapper";
import Footer from "../../UI/Footer";
import classes from "./InvoiceItemDescription.module.css";
import { Link } from "react-router-dom";
const InvoiceItemDescription = (props) => {
let counter = 1;
return (
<Wrapper isShrinked={props.isShrinked}>
<div className={classes.wrapper}>
<div className={classes["content-wrapper"]}>
<div className={classes["main-wrapper"]}>
<div className={classes["upper-buttons"]}>
<div className={classes["upper-buttons-wrapper"]}>
<Link to="/invoices">
<button type="button" className={classes["go-to-invoices"]}>
Go To Invoices
</button>
</Link>
<Link to="/invoices/edit-invoice">
<button type="button" className={classes["edit-invoice"]}>
Edit Invoice
</button>
</Link>
</div>
</div>
<div className={classes.content}>
<div className={classes["invoice-info"]}>
<div className={classes.info}>
<h3>Invoice Info</h3>
<span>{props.invoiceNumber}</span>
</div>
<div className={classes.order}>
<p>
<span className={classes["order-status"]}>
Order Status:
</span>
<span className={classes.status}>{props.status}</span>
</p>
<p>
<span className={classes["order-date"]}>Order Date:</span>
<span className={classes.date}>{props.order_date}</span>
</p>
</div>
</div>
<div className={classes.bills}>
<div className={classes["bill-from"]}>
<h3>Bill From</h3>
<div>
<p className={classes["bill-from-info"]}>
<span className={classes.name}>{props.bill_from}</span>
<span className={classes.email}>
{props.bill_from_email}
<br></br>
<br></br> {props.bill_from_address}
<br></br>
<br></br>
<br></br> {props.bill_from_phone}
</span>
</p>
</div>
</div>
<div className={classes["bill-to"]}>
<h3>Bill To</h3>
<p className={classes["bill-to-info"]}>
<span className={classes.name}>{props.bill_to}</span>
<span className={classes.email}>
{props.bill_to_email} <br></br>
<br></br> {props.bill_to_address} <br></br>
<br></br>
<br></br>
{props.bill_to_fax} <br></br> {props.bill_to_phone}
</span>
</p>
</div>
</div>
<div className={classes.table}>
<table>
<colgroup>
<col className={classes.col1}></col>
<col className={classes.col2}></col>
<col className={classes.col3}></col>
<col className={classes.col4}></col>
<col className={classes.col5}></col>
</colgroup>
<thead>
<tr>
<td>#</td>
<td>Item Name</td>
<td>Unit Costs</td>
<td>Unit</td>
<td>Price</td>
</tr>
</thead>
<tbody>
<tr>
<td>{counter++}</td>
<td>{props.item_name}</td>
<td>{props.unit_costs}</td>
<td>{props.unit}</td>
<td>{props.price}</td>
</tr>
</tbody>
</table>
</div>
<div className={classes.total}>
<p>
Sub-total:
<span>$13300</span>
</p>
<p>
Vat:
<span>$13300</span>
</p>
<h3>
Grand Total:
<span>$14630</span>
</h3>
</div>
</div>
<div className={classes["lower-btn"]}>
<button type="button">Send Invoice</button>
</div>
</div>
</div>
</div>
<Footer />
</Wrapper>
);
};
export default InvoiceItemDescription;
And Invoice.js file
import React from "react";
import classes from "./Invoice.module.css";
import { useDispatch } from "react-redux";
import { Link } from "react-router-dom";
import { invoiceActions } from "../../store/invoice-slice";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faTrash } from "#fortawesome/free-solid-svg-icons";
const Invoice = (props) => {
const { id, invoice_num, bill_from, bill_to, status } = props.invoiceItem;
const dispatch = useDispatch();
const removeInvoiceItem = () => {
dispatch(invoiceActions.removeInvoice(id));
};
return (
<tr className={classes.height}>
<td>
<span className={classes.checkbox}>
<input type="checkbox"></input>
</span>
</td>
<td>
<span>{invoice_num}</span>
</td>
<td>
<span>{bill_from}</span>
</td>
<td>
<span>{bill_to}</span>
</td>
<td>
<span>14300</span>
{/* This should be a dynamic value later */}
</td>
<td>
<span
className={`${
status === "Pending" ? classes["status-pending"] : ""
} ${status === "Delivered" ? classes["status-delivered"] : ""} ${
status === "Shipped" ? classes["status-shipped"] : ""
}`}
>
{status}
</span>
</td>
<td>
<div className={classes.buttons}>
<Link to={`/invoices/invoice-description/${id}`}>
<button className={classes["view-btn"]}>View</button>
</Link>
<button className={classes["delete-btn"]} onClick={removeInvoiceItem}>
<FontAwesomeIcon icon={faTrash} />
</button>
</div>
</td>
</tr>
);
};
export default Invoice;
I have no idea what can cause the crash of the page. Can someone help me with this, please?
P.S. here is my github repo(it's my PET project) - https://github.com/stepan-slyvka/test-project

The issue is that in the INVOICES_LIST test invoice data exported from src/components/Pages/Invoice/InvoicesList.js you are creating randomly generated id properties. When the page reloads, the entire app reloads, INVOICES_LIST is exported with all new id properties. The id that is read from the URL path is no longer valid and InvoiceItem can't render an invoice object that is undefined.
export const INVOICES_LIST = [
{
id: Math.random().toString(), // <-- random each time app loads
...
},
{
id: Math.random().toString(), // <-- random each time app loads
...
},
{
id: Math.random().toString(), // <-- random each time app loads
...
},
];
You really want GUIDs to be stable, and more determinant and guaranteed for uniqueness, so don't use Math.random to create them, use something more like uuid if you need to generate unique ids.
To resolve your specific issue the fix is to just hardcode a unique id value. Even just complete gibberish, so long as it uniquely identifies an object, is sufficient (for testing).
Example:
export const INVOICES_LIST = [
{
id: '09u34otiuhnrfgp9ioj45',
...
},
{
id: '234098ujh43gikoljaerpgiojaerg',
...
},
{
id: '0934tpinr-9ujw3ensdsf',
...
},
];
In the InvoiceItem component that is searching the INVOICES_LIST array keep in mind that Array.prototype.find potentially returns undefined when no match is found. The UI should handle this. Conditionally render the InvoiceItemDescription only if there is a found invoice.
Example:
const InvoiceItem = () => {
const { invoiceId } = useParams();
const invoice = INVOICES_LIST.find((invoice) => invoice.id === invoiceId);
return invoice ? (
<InvoiceItemDescription
invoice_num={invoice.invoice_num}
status={invoice.status}
order_date={invoice.order_date}
bill_from={invoice.bill_from}
bill_from_address={invoice.bill_from_address}
bill_from_email={invoice.bill_from_email}
bill_from_fax={invoice.bill_from_fax}
bill_from_phone={invoice.bill_from_phone}
bill_to={invoice.bill_to}
bill_to_address={invoice.bill_to_address}
bill_to_email={invoice.bill_to_email}
bill_to_fax={invoice.bill_to_fax}
bill_to_phone={invoice.bill_to_phone}
item_name={invoice.ITEMS.item_name}
unit_costs={invoice.ITEMS.unit_costs}
unit={invoice.ITEMS.unit}
price={invoice.ITEMS.price}
/>
) : (
<div>No Invoices Found.</div>
);
};

So, issue is fixed and to conclude, maybe someone is facing the same problem, I'll write a few words.
Firstly, when you have static data like I had(3 invoices which renders each time page loads) - hardcode your id's.
Secondly, for newly created items use stable id's(example - uuid) and store it somewhere(for example in local storage).
Thirdly, if page crashes or something renders wrong - check your state! In my case issue was in InvoiceItem.js file where i was trying to render INVOICES_LIST instead of selecting my state with const invoices = useSelector((state) => state.invoice.invoices)
Hope, this helps someone like me - novice in redux :)
P.S. final code is here

Related

How to pass partial data to a parent component in react

I have the following Component TBorrowed
import React, { Fragment, useState} from "react";
import {Link} from 'react-router-dom';
const EditItem = ({ item }) => {
const [name, setName] = useState(item.name)
const saveData = async (e) => {
e.preventDefault();
const body = { name}
await fetch(`http://127.0.0.1:5000/item/edit/${item.id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
})
}
return (
<Fragment>
<Link className="link" data-toggle="modal" data-target={`#id${item.id}`} >{item.name}</Link>
<div className="modal" id={`id${item.id}`}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">Edit Item</h4>
</div>
<div className="modal-body">
<label>Name</label>
<input value={name} onChange={e => { setName(e.target.value) }} type="text" />
</div>
<div className="modal-footer">
<button onClick={e => { saveData(e) }} type="button" className="btn btn-outline-success ml-auto" data-dismiss="modal">Save</button>
</div>
</div>
</div>
</div>
</Fragment>
)
}
export default EditItem;
The above is called in another component, Main as shown below
import React, { useState} from 'react';
import TBorrowed from './TBorrowed';
const Main = () => {
const [items, setItems] = useState([]);
...MANY ITEMS SKIPPED...
return (
<table className="layout">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Code</th>
</tr>
</thead>
<tbody>
{
items.map((item, index) => (
<tr key={item.id}>
<td>{index + 1}</td>
<td>{item.name}</td>
<td>{<TBorrowed item={item} />}</td>
</tr>
))
}
</tbody>
</table>
)
}
export default Main;
The above works well where I am able to see the item code in the Main component's <td></td> when rendered, which when I click, I am able to edit the particular item in a modal.
My issue is I no longer want to edit an item in a modal but I want it rendered on it's own page for editing.
When I try it without a data-toggle = "modal" in the TBorrowed component, I get all the contents of the TBorrowed component displaying in the Main component where the modal is called i.e <td>{<TBorrowed item={item} />}</td>. All the data in TBorrowed is shown in that <td></td> instead of just the item.code as it was showing while using the modal
My code has some parts missing so it can fit here.
Please assist, and if there's more information required I'll provide it.

How to transfer product items to the payment page after clicking the checkout button in React? [duplicate]

This question already has an answer here:
How do you pass data when using the navigate function in react router v6
(1 answer)
Closed 5 months ago.
Goal: I should transfer all the added product items from the product cart to the payment page after the user will click the checkout button.
Product items inside the product cart:
I should transfer the product items here in the Payment page (after checkout button is clicked):
Problem: I am not sure how to transfer the product items(including its prices, total prices, and increment/decrement buttons) to the payment page, even by using props and replicating mapping functionality of the Basket.jsx(where the cart functionality is found).
I know I shouldn't be replicating the functionality, especially in terms with mapping since there would be one and only one parent component for this.
Source code for Basket.jsx:
import React from "react";
import {
BrowserRouter as Router,
Routes,
Route,
useNavigate
} from "react-router-dom";
export default function Basket(props) {
const navigate = useNavigate();
const navigateToPaymentPage = () => {
navigate("/paymentpage");
};
const { cartItems, onAdd, onRemove } = props;
const itemsPrice = cartItems.reduce((a, c) => a + c.price * c.qty, 0);
const totalPrice = itemsPrice;
// const totalPrice = itemsPrice + discountItemPrice ---- for discount items soon
return (
<aside className="block col-1">
<h2>Cart Items</h2>
{/* Display message when cartItemsLength is 0 */}
<div>{cartItems.length === 0 && <div>Cart is Empty</div>} </div>
{/* Renders the added item to the basket of the shopping cart through mapping cartItems */}
{cartItems.map((item) => (
<div key={item.id} className="row">
<div className="col-2">
{item.name} -- ${item.price.toFixed(2)}
</div>
{/* Increment and Decrement Buttons */}
<div className="col-2">
<button onClick={() => onRemove(item)} className="remove">
-
</button>
<button onClick={() => onAdd(item)} className="add">
+
</button>
Qty: {item.qty}
</div>
<div className="col-2 text-right">
${(item.price * item.qty).toFixed(2)}
</div>
</div>
))}
{cartItems.length !== 0 && (
<>
<hr></hr>
<div className="row">
<div className="col-2">
<strong>Total Price</strong>
</div>
<div className="col-1 text-right">
<strong>${totalPrice.toFixed(2)}</strong>
</div>
</div>
<hr />
<div className="row">
<button onClick={navigateToPaymentPage}>Checkout</button>
</div>
</>
)}
</aside>
);
}
Source code for PaymentPage.jsx:
import React from "react";
import {
BrowserRouter as Router,
Routes,
Route,
useNavigate
} from "react-router-dom";
export default function PaymentPage(props) {
//I replicated the functionality here:
const { cartItems, onAdd, onRemove } = props;
const itemsPrice = cartItems.reduce((a, c) => a + c.price * c.qty, 0);
const totalPrice = itemsPrice;
const navigate = useNavigate();
const navigateToHomeOrderPage = () => {
navigate("/");
};
return (
<aside className="block col-1">
<button
sx={{ width: 10 }}
style={{ maxWidth: "60px" }}
onClick={navigateToHomeOrderPage}
>
Go back
</button>
<h2>PAYMENT PAGE</h2>
{/* Display message when cartItemsLength is 0 */}
<div>{cartItems.length === 0 && <div>Cart is Empty</div>} </div>
{/* Renders the added item to the basket of the shopping cart through mapping cartItems */}
{cartItems.map((item) => (
<div key={item.id} className="row">
<div className="col-2">
{item.name} -- ${item.price.toFixed(2)}
</div>
{/* Increment and Decrement Buttons */}
<div className="col-2">
<button onClick={() => onRemove(item)} className="remove">
-
</button>
<button onClick={() => onAdd(item)} className="add">
+
</button>
Qty: {item.qty}
</div>
<div className="col-2 text-right">
${(item.price * item.qty).toFixed(2)}
</div>
</div>
))}
{cartItems.length !== 0 && (
<>
<hr></hr>
<div className="row">
<div className="col-2">
<strong>Total Price</strong>
</div>
<div className="col-1 text-right">
<strong>${totalPrice.toFixed(2)}</strong>
</div>
</div>
<hr />
</>
)}
</aside>
);
}
Full source codes (functioning App):
https://codesandbox.io/s/productitemsdisplayfixing-cherry-h0br4o-forked-nvu3d0?file=/src/components/PaymentPage.jsx:0-1954
Code Basis: https://www.youtube.com/watch?v=AmIdY1Eb8tY&t=2209s
Your responses would indeed help and guide me a lot since I am very much confused on how to pass these data from one page component to another. Thus, it would be great to hear guides and responses from all of you. Thank you very much!
Just as in the linked answer in the comments, you can pass the data in the state option of the navigate function
const navigate = useNavigate();
const { cartItems, onAdd, onRemove } = props;
const itemsPrice = cartItems.reduce((a, c) => a + c.price * c.qty, 0);
const totalPrice = itemsPrice;
const navigateToPaymentPage = () => {
navigate("/paymentpage", {
state: {
totalPrice,
cartItems
}
});
};
And then get it on the other page from the location object
const navigate = useNavigate();
const location = useLocation();
const navigateToHomeOrderPage = () => {
navigate("/");
};
const data = location.state;
return (
<aside className="block col-1">
<button
sx={{ width: 10 }}
style={{ maxWidth: "60px" }}
onClick={navigateToHomeOrderPage}
>
Go back
</button>
<h2>PAYMENT PAGE</h2>
{JSON.stringify(data)}
</aside>
);
You can check the updated sandbox https://codesandbox.io/s/react-router-dom-pass-state-r1iis7?file=/src/components/Basket.jsx
However, a cart implementation usually uses a backend as a source of truth, but if your project is for learning purposes, I recommend you to also take a look at global state management libraries, the most common is reduxjs in case you need to persist the data through route changes or even between page reloads if you add redux-persist

How to open dynamic modal with react js

I am trying to convert the HTML/Javascript modal to React js.
In Reactjs, I just want to open the modal whenever the user clicks the View Project button.
I have created a parent component (Portfolio Screen) and a child component (Portfolio Modal). The data I have given to the child component is working fine but the modal opens the first time only and then does not open. Another problem is that the data does not load even when the modal is opened the first time.
Codesandbox link is here.
https://codesandbox.io/s/reverent-leftpad-lh7dl?file=/src/App.js&resolutionWidth=683&resolutionHeight=675
I have also shared the React code below.
For HTML/JavaScript code, here is the question I have asked before.
How to populate data in a modal Popup using react js. Maybe with hooks
Parent Component
import React, { useState } from 'react';
import '../assets/css/portfolio.scss';
import PortfolioModal from '../components/PortfolioModal';
import portfolioItems from '../data/portfolio';
const PortfolioScreen = () => {
const [portfolio, setportfolio] = useState({ data: null, show: false });
const Item = (portfolioItem) => {
setportfolio({
data: portfolioItem,
show: true,
});
};
return (
<>
<section className='portfolio-section sec-padding'>
<div className='container'>
<div className='row'>
<div className='section-title'>
<h2>Recent Work</h2>
</div>
</div>
<div className='row'>
{portfolioItems.map((portfolioItem) => (
<div className='portfolio-item' key={portfolioItem._id}>
<div className='portfolio-item-thumbnail'>
<img src={portfolioItem.image} alt='portfolio item thumb' />
<h3 className='portfolio-item-title'>
{portfolioItem.title}
</h3>
<button
onClick={() => Item(portfolioItem)}
type='button'
className='btn view-project-btn'>
View Project
</button>
</div>
</div>
))}
<PortfolioModal portfolioData={portfolio} show={portfolio.show} />
</div>
</div>
</section>
</>
);
};
export default PortfolioScreen;
Child Component
import React, { useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
const PortfolioModal = ({ portfolioData, show }) => {
const portfolioItem = portfolioData;
const [openModal, setopenModal] = useState({ showState: false });
useEffect(() => {
setopenModal({
showState: show,
});
}, [show]);
return (
<>
<div
className={`portfolio-popup ${
openModal.showState === true ? 'open' : ''
}`}>
<div className='pp-inner'>
<div className='pp-content'>
<div className='pp-header'>
<button
className='btn pp-close'
onClick={() =>
setopenModal({
showState: false,
})
}>
<i className='fas fa-times pp-close'></i>
</button>
<div className='pp-thumbnail'>
<img src={portfolioItem.image} alt={`${portfolioItem.title}`} />
</div>
<h3 className='portfolio-item-title'>{portfolioItem.title}</h3>
</div>
<div className='pp-body'>
<div className='portfolio-item-details'>
<div className='description'>
<p>{portfolioItem.description}</p>
</div>
<div className='general-info'>
<ul>
<li>
Created - <span>{portfolioItem.creatDate}</span>
</li>
<li>
Technology Used -
<span>{portfolioItem.technologyUsed}</span>
</li>
<li>
Role - <span>{portfolioItem.Role}</span>
</li>
<li>
View Live -
<span>
<NavLink to='#' target='_blank'>
{portfolioItem.domain}
</NavLink>
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default PortfolioModal;
You don't have to use one useState hook to hold all your states. You can and I think you should break them up. In the PortfolioScreen component
const [data, setData] = useState(null);
const [show, setShow] = useState(false);
I changed the function Item that is used to set the active portfolio item to toggleItem and changed it's implementation
const toggleItem = (portfolioItem) => {
setData(portfolioItem);
setVisible(portfolioItem !== null);
};
You should use conditional rendering on the PortfolioModal, so you won't need to pass a show prop to it, and you'll pass a closeModal prop to close the PortfolioModal when clicked
{visible === true && data !== null && (
<PortfolioModal
data={data}
closeModal={() => toggleItem()} // Pass nothing here so the default value will be null and the modal reset
/>
)}
Then in the PortfolioModal component, you expect two props, data and a closeModal function
const PortfolioModal = ({ data, closeModal }) => {
And the close button can be like
<button className="btn pp-close" onClick={closeModal}>
...

How to create multiple layouts in Next js?

I want to create another layout on my nextjs app. I saw this method somewhere, on adding multiple/nested layout in next js. The problem is I can't make this work, there's always this error:
Error: MyApp(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
Now this is my code:
My _app.js
import Layout from "./../components/Layout";
function MyApp({ Component, pageProps }) {
const getLayout = Component.pageLayout || ((page) => <Layout>{page}</Layout>);
return getLayout(<Component {...pageProps} />);
}
export default MyApp;
This is my Layout
import React, { Fragment, useState, useEffect, useRef } from "react";
import { useRouter } from 'next/router';
import NavBar from "./../NavBar";
import MidNavBar from "./../MidNavBar";
import Banner from "./../Banner";
const Layout = ({ children, navBarRefProps }) => {
const navBarRef = useRef();
const router = useRouter();
const navBarRefPropsHandle = (props) => {
navBarRef.current = props.current;
navBarRefProps(navBarRef.current);
}
return(
<Fragment>
<div className="wrapper">
<NavBar navBarRefProps={ navBarRefPropsHandle } />
<div className="inner_wrapper">
<Banner/>
{
router.pathname !== "/" ?
<MidNavBar midNavBarRefProps = {midNavBarRefPropsHandle} dummyRefProps={dummyRefPropsHandle}/>
:
""
}
{ children }
</div>
</div>
</Fragment>
)
}
This is my AnotherLayout
const AnotherLayout = ({ children }) => {
return(
<Fragment>
<main>
<div>
<aside className="menu">
<div className="menu_header">
<div className="menu_title">
<span>
Panel
</span>
<button onClick={logoutHandle}>
logout
</button>
</div>
<button className="burger" onClick={openMenuHandle}>
<div className="burger-one">
</div>
<div className="burger-two">
</div>
<div className="burger-three">
</div>
</button>
</div>
<div ref={menuBodyRef} className="menu_body">
<ul>
<li>
<Link href={ url + "/rooms" }>
<div>
</div>
News
</Link>
</li>
<li>
<Link href={ url + "/events" }>
<div>
</div>
Events
</Link>
</li>
<li>
<Link href={ url + "/categories" }>
<div>
<i className="fas fa-snowboarding"></i>
</div>
Activities
</Link>
</li>
</ul>
</div>
</aside>
<section className="container_panel">
{ children }
</section>
</div>
</main>
</Fragment>
)
}
export default AnotherLayout;
And this is my index page
import React, { Fragment, useState, useEffect, useRef } from "react";
import AnotherLayout from "./../../../../components/AnotherLayout";
const Panel = () => {
// a lot of codes here.....
return(
<Fragment>
<section>
<div>
<div>
<h1><span>Rooms</span> Panel</h1>
</div>
<div className="container_table">
<table>
<thead>
<tr>
<td>Rooms</td>
<td>
<div>
<input type="search" name="search" placeholder="search"/>
</div>
</td>
<td className="col_add">
<button>
add
rooms
</button>
</td>
</tr>
</thead>
<tbody>
{
rooms.map(room => {
return(
<tr key={room._id}>
<td colSpan="2">
<button>{room.title}</button>
</td>
<td className="ud">
<button className="edit">
edit
</button>
<button className="delete">
delete
</button>
</td>
</tr>
)
})
}
</tbody>
<tfoot>
<tr>
<td>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</section>
</Fragment>
)
}
Panel.getLayout = (page) => {
<AdminLayout>
{ page }
</AdminLayout>
}
export default Panel;
Thank you!.
Solved this by adding return()
Panel.getLayout = (page) => {
return(
<AdminLayout>
{ page }
</AdminLayout>
)
}

Modify the state with other component for the input in React

I carry out a project which can modify the price of a product (recovered from a fake API) and then at the click of a button carries out the update by calculating the VAT of 20%. I encounter a problem I would like to have a price state and that in this state it's the value of my input namely {listProduct.price} but it doesn't work.
If you have solutions, I am interested, thank you in advance. (sorry I'm new to React I still have a bit of trouble with all these concepts)
import React, { Component } from 'react'
import '../css/ProductsDetails.css'
import {AiOutlineArrowLeft} from "react-icons/ai";
import {Link} from 'react-router-dom'
export default class ProductsDetails extends Component {
state = {
id: this.props.match.params.id,
price:
}
updatePrice = (e) => {
console.log(e);
this.setState({
price: e.target.value
})
}
render() {
const {location: {state: {listProduct}}} = this.props;
return (
<div className="products__details">
<Link to="/"><AiOutlineArrowLeft className="nav__arrow" /></Link>
<h1 className="details__title">{listProduct.title}</h1>
<div className="details__align--desk">
<div className="details__img">
<img className="product__img" src={listProduct.image} alt="Affichage du produit"/>
</div>
<div className="products__align--desk">
<h2 className="product__title">Description</h2>
<p className="product__description">{listProduct.description}</p>
<h2 className="product__title">Price</h2>
<form className="form__price">
<input className="input__price" type="text" value={listProduct.price} onChange={this.updatePrice} />
<p>Price (including VAT): {Math.round((listProduct.price + listProduct.price * 0.2)*100) /100} €</p>
<br/>
<input className="btn__update" type="submit" value="Update product" />
</form>
</div>
<div className="category__align--desk">
<h2 className="product__title">Category</h2>
<p className="product__category">{listProduct.category}</p>
</div>
</div>
</div>
)
}
}
export default class Products extends Component {
constructor(props) {
super(props);
this.state = {productsData: []};
}
componentDidMount = () => {
axios.get('https://fakestoreapi.com/products?limit=7')
.then(res => {
console.log(res.data)
this.setState ({
productsData: res.data
})
})
}
render() {
const listsProducts = this.state.productsData.map(listProduct => {
return <tbody className="products__body">
<tr>
<td> <Link to={{pathname: "/products-details/" + listProduct.id,state: {listProduct}}}>{listProduct.title}</Link></td>
<td className="products__category">{listProduct.category}</td>
<td>{listProduct.price}</td>
<td>{Math.round((listProduct.price + listProduct.price * 0.2)*100) /100}</td>
</tr>
</tbody>
})
return (
<main className="products">
<h1 className="products__title">Products management</h1>
<table cellSpacing="0">
<thead className="products__head">
<tr>
<th className="table--title">Product name</th>
<th className="table--title">Category</th>
<th className="table--title">Price</th>
<th className="table--title">Price (including VAT)</th>
</tr>
</thead>
{listsProducts}
</table>
</main>
)
}
}
Inside a react component:
1 - You declare the initial state of your component, which is, in this case, the price that the product has before the user writes something. For now, we'll set it to 0:
state = {
id: this.props.match.params.id,
price: this.props.listProduct.price ? this.props.listProduct.price : 0
}
2 - Then, in the render method, we access the price value from this.state
3 - Finally, we modify our input element so that it gets the value of the price.
<input className="input__price" type="text" value={price} onChange={this.updatePrice} />
The rest of the component was working well.
This is the result:
import React, { Component } from 'react'
import '../css/ProductsDetails.css'
import {AiOutlineArrowLeft} from "react-icons/ai";
import {Link} from 'react-router-dom'
export default class ProductsDetails extends Component {
state = {
id: this.props.match.params.id,
price: '0'
}
updatePrice = (e) => {
console.log(e);
this.setState({
price: e.target.value
})
}
render() {
const {price} = this.state
return (
<div className="products__details">
<Link to="/"><AiOutlineArrowLeft className="nav__arrow" /></Link>
<h1 className="details__title">{listProduct.title}</h1>
<div className="details__align--desk">
<div className="details__img">
<img className="product__img" src={listProduct.image} alt="Affichage du produit"/>
</div>
<div className="products__align--desk">
<h2 className="product__title">Description</h2>
<p className="product__description">{listProduct.description}</p>
<h2 className="product__title">Price</h2>
<form className="form__price">
<input className="input__price" type="text" value={price} onChange={this.updatePrice} />
<p>Price (including VAT): {Math.round((listProduct.price + listProduct.price * 0.2)*100) /100} €</p>
<br/>
<input className="btn__update" type="submit" value="Update product" />
</form>
</div>
<div className="category__align--desk">
<h2 className="product__title">Category</h2>
<p className="product__category">{listProduct.category}</p>
</div>
</div>
</div>
)
}
}
Start off with the price at 0 (not in quotes) in state, and then...
const price = this.state.price || (this.props.listProduct ? this.props.listProduct.price : 0)
<input className="input__price" type="text" value={price} onChange{this.updatePrice} />
So if the state value has been updated, that will be used, if not it will check if the price is available in props and use that, and if not it will display zero.

Resources