How to hide header and footer for a specific Route - reactjs

I have a OrderPrintReceiptScreen, on loading this screen I want the header and footer to not show on the screen. And after that I want to use window.print(); And this way a clean PDF Receipt can be achieved. But due to header and Footer they make it very dense and I'm not sure how to remove that on loading this OrderPrintReceiptScreen.
This is the layout of App
function App() {
return (
<Router>
<Header />
<main className="py-1">
<Container>
//.....
<Route path="/order-receipt/:id" component={OrderPrintReceiptScreen} />
//.....
</Container>
</main>
<Footer />
</Router>
);
}
export default App;
OrderPrintReceiptScreen.js
import React, { useEffect } from "react";
import { Button, Row, Col, ListGroup } from "react-bootstrap";
import { Page, Text, View, Document, StyleSheet } from "#react-pdf/renderer";
import { LinkContainer } from "react-router-bootstrap";
import { useDispatch, useSelector } from "react-redux";
import Message from "../components/Message";
import Loader from "../components/Loader";
import {
getOrderDetails,
// payOrder,
} from "../actions/orderActions";
import {
ORDER_PAY_RESET,
ORDER_DELIVER_RESET,
} from "../constants/orderConstants";
// Create styles
const styles = StyleSheet.create({
page: {
flexDirection: "row",
backgroundColor: "#E4E4E4",
},
section: {
margin: 10,
padding: 10,
flexGrow: 1,
},
});
function OrderPrintReceiptScreen({ match, history }) {
const orderId = match.params.id;
const dispatch = useDispatch();
const orderDetails = useSelector((state) => state.orderDetails);
const { order, error, loading } = orderDetails;
const orderPay = useSelector((state) => state.orderPay);
const { loading: loadingPay, success: successPay } = orderPay;
const orderDeliver = useSelector((state) => state.orderDeliver);
const { loading: loadingDeliver, success: successDeliver } = orderDeliver;
const userLogin = useSelector((state) => state.userLogin);
const { userInfo } = userLogin;
if (!loading && !error) {
order.itemsPrice = order.orderItems
.reduce((acc, item) => acc + item.price * item.qty, 0)
.toFixed(2);
}
useEffect(() => {
if (!userInfo) {
history.push("/login");
}
if (
!order ||
successPay ||
order._id !== Number(orderId) ||
successDeliver
) {
dispatch({ type: ORDER_PAY_RESET });
dispatch({ type: ORDER_DELIVER_RESET });
dispatch(getOrderDetails(orderId));
}
}, [dispatch, order, orderId, successPay, successDeliver]);
const printReceipt = (e) => {
e.preventDefault();
window.print();
};
return loading ? (
<Loader />
) : error ? (
<Message variant="danger">{error}</Message>
) : (
<Page size="A4" style={styles.page}>
<View style={styles.section}>
<Text>Section #1</Text>
</View>
<View style={styles.section}>
<Text>Section #2</Text>
</View>
<Row>
<Col md={10}>
<ListGroup variant="flush">
<ListGroup.Item>
<LinkContainer to={`/order-receipt/${order._id}`}>
<Button
variant="outline-success"
className="mx-4 my-4 btn-lg"
fluid
onClick={printReceipt}
>
Download Receipt
</Button>
</LinkContainer>
</ListGroup.Item>
<ListGroup.Item>Order ID : {order._id}</ListGroup.Item>
<ListGroup.Item>
Created On : {order.createdAt.substring(0, 10)},{" "}
{order.createdAt.substring(11, 19)}
</ListGroup.Item>
<ListGroup.Item>
Order Items:
{order.orderItems.length === 0 ? (
<Message variant="info">Order is empty</Message>
) : (
<ListGroup flush>
{order.orderItems.map((item, index) => (
<ListGroup.Item key={index}>
<Row>
<Col>{item.name}</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
)}
</ListGroup.Item>
<ListGroup variant="flush">
<ListGroup.Item>Name : {order.user.name}</ListGroup.Item>
<ListGroup.Item>
Phone Number : {order.shippingAddress.phoneNumber}
</ListGroup.Item>
<ListGroup.Item>
Shipping Address : {order.shippingAddress.address},{" "}
{order.shippingAddress.city}
{" "}
{order.shippingAddress.postalCode},{" "}
{order.shippingAddress.country}
</ListGroup.Item>
{order.isPaid ? (
<Message variant="light">
Payment Status : Paid On {order.paidAt.substring(0, 10)},{" "}
{order.paidAt.substring(11, 19)}
</Message>
) : (
<Message variant="warning">Not Paid</Message>
)}
<ListGroup variant="flush">
<ListGroup.Item>Payment Summary : </ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Items Price :</Col>
<Col>PKR {order.itemsPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Shipping Price :</Col>
<Col>PKR {order.shippingPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Tax Amount :</Col>
<Col>PKR {order.taxPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Total Payable :</Col>
<Col> PKR {order.totalPrice}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Total Paid :</Col>
<Col> PKR {order.totalPricePaid}</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>Remaining Amount:</Col>
<Col>
{" "}
PKR {Number(order.totalPrice) - order.totalPricePaid}
</Col>
</Row>
</ListGroup.Item>
</ListGroup>
{order.isDelivered ? (
<Message variant="light">
Delivery Status : Delivered on{" "}
{order.deliveredAt.substring(0, 10)},{" "}
{order.deliveredAt.substring(11, 19)}
</Message>
) : (
<Message variant="warning">Not Delivered</Message>
)}
</ListGroup>
</ListGroup>
</Col>
{/* <Col md={4}>
</Col> */}
</Row>
</Page>
);
}
export default OrderPrintReceiptScreen;

There are 2 ways to do this:
The first method is to check your matching URL before rendering:
render() {
const {match: {url}} = this.props;
if(url.startWith('/ignore-header-path') {
return null;
} else {
// your render jsx
}
}
The second method is to use #media print:
#media print {
/* Your print styles */
.header, .footer { display: none !important; }
}

You need to create a component Like a layout inside layout you can manage conditional header and footer Like this example.
Remove the Header footer from the App file.
Have a look I hope it's helpful
const OrderPrintReceiptScreen= (props) => {
return (
<Layouts
showFooter={false}
showHeader={false}
>
<Childeren {...props} />
</Layouts>
);
};
const Layouts= ({showFooter,showHeader,children}) =>{
return (
{showHeader && <Header/>}
{children}
{showFooter && <Footer/>}
)
}
........

You can use the window.location.pathname to get the current route after that do the validation on the route if the same hide the header else show the header.
{
window.location.pathname!=="/login"? <Header/> : null
}

Related

Can't add product into Cart

Unable to add product into the cart although, the url is getting changed, it showing two errors in the console, cant understand what is wrong.
http://localhost:3000/cart/636d4264ff92e60977f3bb4a?qt=3
636d4264ff92e60977f3bb4a = productID and qt=3 quantity
enter image description here
here are my files
import React, { useEffect } from 'react'
import { Link, useParams, useLocation, useNavigate } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
import { Row, Col, ListGroup, Image, Form, Button, Card } from 'react-bootstrap'
import Message from '../components/Message'
import { addToCart, removeFromCart } from '../actions/cartActions'
const CartScreen = ({match, history}) => {
const {id} = useParams();
//const productId = match.params.id
const location = useLocation();
const qty = location.search ? Number (location.search.split('=') [1]) : 1
const dispatch = useDispatch()
const navigate = useNavigate()
const cart = useSelector((state) => state.cart)
const { cartItems } = cart
useEffect(() => {
if(id){
//useEffect working fine
dispatch(addToCart(id,qty))
}else{
//console.log('useEffect error')
}
}, [dispatch, id, qty])
const removeFromCartHandler = (id) => {
dispatch(removeFromCart(id))
}
const chechoutHandler = () => {
navigate('/login?redirect=/shipping')
}
return (
<Row>
<Col md={8}>
<h1>Shopping Cart</h1>
{ cartItems && cartItems.length === 0 ? (
<Message> Your cart is empty <Link to='/'>Go Back</Link></Message>
) : (
<ListGroup variant='flush'>
{cartItems.map(item => (
<ListGroup.Item key={item.product}>
<Row>
<Col md={2}>
<Image src={item.image} alt={item.name} className="cartItemPhoto" flude rounded />
</Col>
<Col md={3}>
<Link to={`/product/${item.product}`}>{item.name}</Link>
</Col>
<Col md={2}>₹{item.price}</Col>
<Col md={2}>
<Form.Control
as='select'
value={item.qty}
onChange={(e)=> dispatch(addToCart(item.product,Number(e.target.value)))}
>
{[...Array(item.countInStock).keys()].map((x) => (
<option key={x+1} value={x + 1}>
{ x + 1 }
</option>
))}
</Form.Control>
</Col>
<Col md={2}>
<Button type='button' varient='ligth' onClick={() => removeFromCartHandler(item.product)} >
<i className='fas fa-trash'></i>
</Button>
</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
)}
</Col>
<Col md={4}>
<Card>
<ListGroup varient = 'flush'>
<ListGroup.Item>
<h2>Subtotal ({cartItems.reduce((acc,item)=>acc+item.qty,0)}) items</h2>
₹{cartItems.reduce((acc,item)=>acc+item.qty * item.price,0).toFixed(2)}
</ListGroup.Item>
<ListGroup>
<Button type='button' className='btn-block' disabled={cartItems.length === 0} onClick={chechoutHandler}>
Proceed To Checkout
</Button>
</ListGroup>
</ListGroup>
</Card>
</Col>
</Row>
)
}
export default CartScreen
import React, { useState, useEffect } from 'react'
import { Link, useParams} from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
import {Row, Col, Image, ListGroup, Card, Button, Form } from 'react-bootstrap'
import Rating from '../components/Rating'
import Loader from '../components/Loader'
import Message from '../components/Message'
import { listProductDetails, createProductReview } from '../actions/productActions'
import { useNavigate } from 'react-router-dom'
//import products from '../../../backend/data/products.js'
//import axios from 'axios'
import { PRODUCT_CREATE_REVIEW_RESET } from '../constants/productConstants'
const ProductScreen = ({ match}) => {
const {id} = useParams();
//const product = products.find((p) => p._id === id);
const [qty, setQty] = useState(1)
const [rating, setRating] = useState(0)
const [comment, setComment] = useState('')
const dispatch = useDispatch()
const productDetails = useSelector(state => state.productDetails)
const { loading, error, product } = productDetails
//console.log(product)
// reviews aasche sudhu
const userLogin = useSelector((state) => state.userLogin)
const { userInfo } = userLogin
const productReviewCreate = useSelector((state) => state.productReviewCreate)
const {
success: successProductReview,
loading: loadingProductReview,
error: errorProductReview,
} = productReviewCreate
useEffect(()=>{
if(successProductReview){
alert('Review Submitted!')
setRating(0)
setComment('')
dispatch({type: PRODUCT_CREATE_REVIEW_RESET})
}
dispatch(listProductDetails(id))
}, [dispatch, id, successProductReview])
//if(!product) return null;
// return ( <div>{product.name}</div> );
const history = useNavigate()
const addToCartHandler = () =>{
history(`/cart/${id}?qt=${qty}`)
}
const submitHandler = (e) => {
e.preventDefault()
dispatch(
createProductReview(id, {
rating,
comment,
})
)
}
//console.log(product)
return (
<>
<Link className='btn btn-outline-primary' to='/'>
Go Back
</Link>
{ loading ? (
<Loader/>
): error ? (
<Message variant='danger'> {error} </Message>
) : (
<>
<Row>
<Col md={6}>
{/* src={product.image} changed to -> {`${window.location.origin}/${product.image.name}`} */}
<Image src= {product.image} alt={product.name} fluid/>
</Col>
<Col md={3}>
<ListGroup variant='flust'>
<ListGroup.Item>
<h3>{product.name}</h3>
</ListGroup.Item>
<ListGroup.Item>
{/*
<Rating
value={product.rating.toString()}
text={product.numReviews}
/>
*/}
</ListGroup.Item>
<ListGroup.Item>
Price: ₹{product.price}
</ListGroup.Item>
<ListGroup.Item>
Description: {product.description}
</ListGroup.Item>
</ListGroup>
</Col>
<Col md={3}>
<Card>
<ListGroup variant='flush'>
<ListGroup.Item>
<Row>
<Col>
Price:
</Col>
<Col>
<strong>₹{product.price}</strong>
</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col>
Status:
</Col>
<Col>
{product.countInStock>0 ? 'In Stock' : 'Out Of Stock'}
</Col>
</Row>
</ListGroup.Item>
{ product.countInStock > 0 && (
<ListGroup.Item>
<Row>
<Col>Qty</Col>
<Col>
<Form.Control
as='select'
value={qty}
onChange={(e)=>setQty(e.target.value)}
>
{[...Array(product.countInStock).keys()].map((x) => (
<option key={x+1} value={x + 1}>
{ x + 1 }
</option>
))}
</Form.Control>
</Col>
</Row>
</ListGroup.Item>
)}
<ListGroup.Item>
<Button
onClick={addToCartHandler}
className='btn btn-success btn-block btn-wd'
type='button'
disabled={product.countInStock===0}
>
Add To Cart
</Button>
</ListGroup.Item>
</ListGroup>
</Card>
</Col>
</Row>
<Col md={6}>
<h2>Reviews</h2>
{product.reviews.length === 0 && <Message>No Reviews</Message>}
<ListGroup variant='flush'>
{product.reviews.map((review) => (
<ListGroup.Item key={review._id}>
<strong>{review.name}</strong>
<Rating value={review.rating} />
<p>{review.createdAt.substring(0, 10)}</p>
<p>{review.comment}</p>
</ListGroup.Item>
))}
<ListGroup.Item>
<h2>Write a Customer Review</h2>
{successProductReview && (
<Message variant='success'>
Review submitted successfully
</Message>
)}
{loadingProductReview && <Loader />}
{errorProductReview && (
<Message variant='danger'>{errorProductReview}</Message>
)}
{userInfo ? (
<Form onSubmit={submitHandler}>
<Form.Group controlId='rating'>
<Form.Label>Rating</Form.Label>
<Form.Control
as='select'
value={rating}
onChange={(e) => setRating(e.target.value)}
>
<option value=''>Select...</option>
<option value='1'>1 - Poor</option>
<option value='2'>2 - Fair</option>
<option value='3'>3 - Good</option>
<option value='4'>4 - Very Good</option>
<option value='5'>5 - Excellent</option>
</Form.Control>
</Form.Group>
<Form.Group controlId='comment'>
<Form.Label>Comment</Form.Label>
<Form.Control
as='textarea'
row='3'
value={comment}
onChange={(e) => setComment(e.target.value)}
></Form.Control>
</Form.Group>
<Button
disabled={loadingProductReview}
type='submit'
variant='primary'
>
Submit
</Button>
</Form>
) : (
<Message>
Please <Link to='/login'>sign in</Link> to write a review{' '}
</Message>
)}
</ListGroup.Item>
</ListGroup>
</Col>
</>
)}
</>
)
}
// Form.Control e add chilo, onChange{(e) => setQty(e.target.value)}
/*
<Form.Control
as='select'
value={qty}
onChange{(e) => setQty(e.target.value)}
>
// lec 32
*/
export default ProductScreen
import axios from 'axios'
import {
CART_ADD_ITEM,
CART_REMOVE_ITEM,
CART_SAVE_SHIPING_ADDRESS,
CART_SAVE_PAYMENT_METHOD
} from '../constants/cartConstants'
import { useParams } from 'react-router-dom'
export const addToCart = (id, qty) => async (dispatch, getState) => {
const {id} = useParams();
//console.log(`id working = ${id}`)
const { data } = await axios.get(`/api/products/${id}`)
//console.log(`data = ${data}`)
dispatch({
type: CART_ADD_ITEM,
payload: {
product: data._id,
name: data.name,
image: data.image,
price: data.price,
countInStock: data.countInStock,
qty,
},
})
localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems))
}
export const removeFromCart = (id) => (dispatch, getState) => {
dispatch({
type: CART_REMOVE_ITEM,
payload: id
})
localStorage.setItem('cartItems',JSON.stringify(getState().cart.cartItems))
}
export const saveShippingAdderss = (data) => (dispatch) => {
dispatch({
type: CART_SAVE_SHIPING_ADDRESS,
payload: data,
})
localStorage.setItem('shippingAddress',JSON.stringify(data))
}
export const savePaymentMethod = (data) => (dispatch) => {
dispatch({
type: CART_SAVE_PAYMENT_METHOD,
payload: data,
})
localStorage.setItem('paymentMethod',JSON.stringify(data))
}
import { CART_ADD_ITEM, CART_REMOVE_ITEM, CART_SAVE_PAYMENT_METHOD, CART_SAVE_SHIPING_ADDRESS } from '../constants/cartConstants.js'
import { PRODUCT_DETAILS_REQUEST, PRODUCT_LIST_REQUEST } from '../constants/productConstants.js'
export const cartReducer = (
state = { cartItems: [], shippingAddress: {} }, action) => {
//console.log(`action.type line on 6 = ${action.type}`)
switch(action.type){
case CART_ADD_ITEM:
//console.log(`CART_ADD_ITEM 3333 = ${action.type}`)
const item = action.payload
const existItem = state.cartItems.find( (x) => x.product === item.product)
if(existItem){
return {
...state,
cartItems: state.cartItems.map ((x) =>
x.product === existItem.product ? item : x
),
}
} else {
return {
...state,
cartItems: [...state.cartItems, item],
}
}
case CART_REMOVE_ITEM:
return {
...state,
cartItems: state.cartItems.filter((x) => x.product !==action.payload),
}
case CART_SAVE_SHIPING_ADDRESS:
return {
...state,
shippingAddress: action.payload,
}
case CART_SAVE_PAYMENT_METHOD:
return {
...state,
paymentMethod: action.payload,
}
default:
return state
}
}
I want to resolve the error so that I can add products to my cart. Thank you

Encountered two children with the same key...ecommerce website

why is it that there are two children with the same key
Im using React and Im trying to make ecommerce website
I dont understand the error of double keys
import React, {useEffect} from 'react'
import { Link, useParams, useNavigate, useLocation, useSearchParams } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
import { Row, Col, ListGroup, Image, Form, Button, Card} from 'react-bootstrap'
import Message from '../components/Message'
import { addToCart } from '../actions/cartActions'
export default function CartScreen() {
const { id} = useParams()
const { search } = useLocation();
const [searchParams] = useSearchParams();
const dispatch = useDispatch();
const productID = id;
const qty = search ? Number(search.split("=")[1]) : 1;
const cart = useSelector(state => state.cart)
const { cartItems} = cart
console.log('cartItems:', cartItems)
useEffect(() => {
if(productID) {
dispatch(addToCart(productID, qty))
}
}, [dispatch, productID, qty])
return (
<Row>
<Col md={8}>
<h1>Shopping Cart</h1>
{cartItems.length === 0 ? (
<Message variant='info'>
Your cart is empty <Link to='/'>Go Back</Link>
</Message>
) : (
<ListGroup varient='flush'>
{cartItems.map(item => (
<ListGroup.Item key= { item.product }>
<Row>
<Col md={2}>
<Image src={item.image} alt={item.name} fluid rounded/>
</Col>
<Col md={3}>
<Link to={`/product/${item.product}`}>{item.name}</Link>
</Col>
<Col md={2}>
${item.price}
</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>
)}
</Col>
<Col md={4}>
</Col>
</Row>
)
}
Im trying to load up the cart images in the CartScreen
and its telling me that there are two children with same key
In React while using the following
<ListGroup.Item key= { uniqueID }>
Every key value has to be unique such that it can identify each item in the list uniquely.
More help from this thread.
key is not unique for the elements rendered, React found two elements with same key. Could you please try modifying the map JSX inside map.
<ListGroup varient='flush'>
{cartItems.map((item, index) => (
<ListGroup.Item key= { `${item.product}i${index}` }>
<Row>
<Col md={2}>
<Image src={item.image} alt={item.name} fluid rounded/>
</Col>
<Col md={3}>
<Link to={`/product/${item.product}`}>{item.name}</Link>
</Col>
<Col md={2}>
${item.price}
</Col>
</Row>
</ListGroup.Item>
))}
</ListGroup>

hide buttons from interface

I have a modal, and this modal has two interfaces, the first is “QRReader” and the second is “PatientForm”, and the modal has two buttons, the first is “Approve” and the second is “Cancel”.
And I want to hide the two buttons within the interface of the "QRReader"
How can i solve the problem?
And this file contains the entire modal, knowing that the BasicModal tag is modal
import { Button, Col, Row } from "antd";
import {
useState
} from "react";
import { QrReader } from "react-qr-reader";
import patient from "../../../api/nuclearMedicineApi/services/Patient";
import { decrypt } from "../../../utils/decryption";
import PatientForm from "./form";
import { QrcodeOutlined } from '#ant-design/icons';
import BasicModal from "../modal";
import { FormattedMessage } from "react-intl";
import { notify } from "../notification";
const QRScanner = () => {
const [data, setData] = useState<number>(0);
const [patientInfoData, setPatientInfoData] = useState({})
const [modelVisible, setModelVisible] = useState<any>();
console.log('datadatadata: ', data)
const openNotificationWithIcon = () => {
// onSuccess: (data) => {
notify('success', 'ok', 'approve-message');
// },
};
return (
<>
<QrcodeOutlined
className='cursor-pointer'
style={{ fontSize: '2rem' }}
color={'#fff'}
onClick={(e) => {
e.stopPropagation()
setModelVisible(true)
}}
/>
<BasicModal
header={<>
<h2 className='text-primary'><FormattedMessage id="qr-scanner" /></h2>
</>}
content={
<>
{
data !=0 ?
<PatientForm patientInfoData={patientInfoData} data={data} />
:
<Row>
<Col span={18} offset={3}>
<QrReader
onResult={async (result: any, error) => {
if (!!result) {
const t = result?.text;
const d = decrypt(t);
let zz: any = d.match(/(\d+)/)
Math.floor(zz[0])
setData(zz[0]);
const pationInfo = await patient.patientGet({ Id: Number(zz[0]) })
setPatientInfoData(pationInfo)
}
if (!!error) {
console.info(error);
}
}} // style={{ width: '100%' }}
constraints={{ facingMode: 'user' }}
// style={{ width: '100%' }}
/>
</Col>
</Row>
}
<Row>
<Col span={1} offset={3}>
<Button
type='primary'
className='savebtn'
onClick={() => {
patient.switchToPresent(data || 0)
openNotificationWithIcon()
}}
>
<FormattedMessage id={'approve'} />
</Button>
</Col>
<Col span={8} offset={4}>
<Button
type='default'
className='savebtn'
onClick={() => {
setModelVisible(false);
setData(0);
}}
>
<FormattedMessage id={'cancel'} />
</Button>
</Col>
</Row>
</>
}
isOpen={modelVisible}
footer={false}
width='50vw'
handleCancel={() => {
setModelVisible(false);
}}
afterClose={
() => setData(0)
}
/>
</>
)
};
export default QRScanner;
I think you should be able to use a similar condition as you are using to determine if you should render patientForm vs QRReader. You could wrap your buttons with something like this
{ data = 0 && (
<Row>
<Col span={1} offset={3}>
<Button
type='primary'
className='savebtn'
onClick={() => {
patient.switchToPresent(data || 0)
openNotificationWithIcon()
}}
>
<FormattedMessage id={'approve'} />
</Button>
</Col>
<Col span={8} offset={4}>
<Button
type='default'
className='savebtn'
onClick={() => {
setModelVisible(false);
setData(0);
}}
>
<FormattedMessage id={'cancel'} />
</Button>
</Col>
</Row>
)
}
You can have the same condition for showing the buttons which you have for QRScanner and PatientForm
{data != 0 ? (
<Row>
<Col span={1} offset={3}>
<Button
type='primary'
className='savebtn'
onClick={() => {
patient.switchToPresent(data || 0)
openNotificationWithIcon()
}}
>
<FormattedMessage id={'approve'} />
</Button>
</Col>
<Col span={8} offset={4}>
<Button
type='default'
className='savebtn'
onClick={() => {
setModelVisible(false);
setData(0);
}}
>
<FormattedMessage id={'cancel'} />
</Button>
</Col>
</Row>
) : </>}

What means each child in the list should have key id

I think i dont understand sth.
I put every where nano id keys but still get error.
react-jsx-dev-runtime.development.js:117 Warning: Each child in a list should have a unique "key" prop.
Check the render method of Users.
import React from "react";
import {useQuery} from "#apollo/client"
import { ListGroup, Container, Row, Col,Card } from "react-bootstrap";
import {GET_USERS} from "../Queries/Queries"
import { nanoid } from 'nanoid'
function Users() {
const { loading, error, data}= useQuery(GET_USERS)
if (loading) return <p>Loading...</p>
if (error) return <p>Error</p>
return (
<Container>
{
data && data.users.map(user=>{
return(
<>
<br/>
<Row key={nanoid()}>
<Card key={nanoid()} style={{ width: '6rem' }}>
<Card.Img key={nanoid()} variant="top" src={user.avatar} />
</Card>
<br />
<Col key={nanoid()}>
<ListGroup key={nanoid()}>
<ListGroup.Item key={nanoid()}>Id: {user.id} </ListGroup.Item>
<ListGroup.Item key={nanoid()}>Email: {user.email}</ListGroup.Item>
<ListGroup.Item key={nanoid()}>Username: {user.username}</ListGroup.Item>
</ListGroup>
</Col>
</Row>
</>
)
})
}
</Container>
);
}
export default Users;
The error message is saying that each item it is mapping needs a key so you just need to add one key to the parent container like so
function Users() {
const { loading, error, data}= useQuery(GET_USERS)
if (loading) return <p>Loading...</p>
if (error) return <p>Error</p>
return (
<Container>
{
data && data.users.map((user, index)=>{
return(
<Row key={index}>
<Card style={{ width: '6rem' }}>
<Card.Img variant="top" src={user.avatar} />
</Card>
<br />
<Col>
<ListGroup>
<ListGroup.Item>Id: {user.id} </ListGroup.Item>
<ListGroup.Item>Email: {user.email}</ListGroup.Item>
<ListGroup.Item>Username: {user.username}</ListGroup.Item>
</ListGroup>
</Col>
</Row>
)
})
}
</Container>
);
}
Instead of adding a parent container to what you had already I just remove dthe JSX tags and the BR. You should add a className to that row to get the desired space instead of using BR.
When you map somthing in react, react track nodes with unique keys. e.g
[{id:1,value:'value 1'},{id:2,value:'value 2'}].map((item,index) =>
(<Row key={`row-${item.id}`}>all content goes here.</Row>))
This key={row-${item.id}} should be unique for every looped Item.

How do you pass data from one child component to another sibling?

My goal is to display different values in the < h1 > of the GoalsDone component depending on which TeamCard is being hovered over. Both of these components are rendered inside of the TopGroups component, in these code snippets I am attempting to pass through the parent TopGroups.
Child component displaying the number of goals done:
const GoalsDone = ({displayGoals}) => {
return (
<GameifyStyle className="">
<Col className="GPM">
<img className="pt-3 mx-auto pl-0" src="../images/bullseye.png" />
<h1 className="pt-1"> {displayGoals}75 Goals</h1>
<p>DONE</p>
</Col>
</GameifyStyle>
)
}
Child Component that updates the score after being hovered over:
It currently has an unhandled runtime error "setDisplayGoals is not a function"
const TeamCard = ({data}, {setDisplayGoals}) => {
return (
<TeamCardStyle>
{!data && (
<Spinner />
)}
{data && data.getGroupScores && (
data.getGroupScores.slice(0, 4).map((group, index) => {
return (
<Row onMouseEnter={() => {setDisplayGoals(group.totalScore)}}>
<Col className="teamCard mt-2 mb-2">
<Row>
<p>{seed[index]}</p>
</Row>
<Row>
<Col className="hideSmall">
<img className="mouseOn" src="../images/group.png" />
<img className="mouseOff" src="../images/groupSelected.png" />
</Col>
</Row>
<p>{group.name}</p>
</Col>
</Row>
)
})
)}
</TeamCardStyle>
)
}
Parent component:
ATTN lines 38, 48
const GET_GROUP_SCORES = gql`
query GET_GROUP_SCORES($quarter: String, $groupId: String) {
getGroupScores(quarter: $quarter, groupId: $groupId) {
name
id
totalScore
goalsDone
milestonesDone
}
}
`;
const TopGroups = () => {
const {loading, error, data } = useQuery(GET_GROUP_SCORES, {variables: { quarter: "Q2 2021" }})
if (data) {
const sortedGroups = data.getGroupScores.sort((a, b) => {
if (a.totalScore > b.totalScore) {
return -1
}
if (a.totalScore < b.totalScore) {
return 1
} else {
return 0
}
})
}
if (error) {
return <p>An error has occured</p>
}
if (loading) {
<Spinner />
}
const [displayGoals, setDisplayGoals] = useState('0');
return (
<Col className="col-12">
<TeamCardStyle>
<Row>
<TeamCard
data={data}
setDisplayGoals={setDisplayGoals}
/>
</Row>
</TeamCardStyle>
<GameifyStyle>
<Row className="cardContainer mt-3 XsWidthAdjust">
<Col className="SideBorder TopGroupsFonts mx-1">
<GoalsDone
displayGoals={displayGoals} />
</Col>
<Col className="SideBorder TopGroupsFonts mx-1">
<PrizesWon />
</Col>
<Col className="SideBorderPH TopGroupsFonts mx-1">
<MilestonesOnTrack />
</Col>
</Row>
</GameifyStyle>
</Col>
)
}
The error "Child Component that updates the score after being hovered over: It currently has an unhandled runtime error "setDisplayGoals is not a function"" happens because you are destructuring the props wrong in your TeamCard component. Instead of doing
const TeamCard = ({data}, {setDisplayGoals}) => {
You should do:
const TeamCard = ({data, setDisplayGoals}) => {

Resources