I have a list of fruits I want show their details. I have built the cards to display the list, and now I want to build a page that shows details for every item and open up its corresponding card list is clicked. I keep getting an error
TypeError: Cannot read properties of undefined (reading 'name')
The list cards work fine, and the individual item api also works fine. But now I cant display the details. Kindly help.
The Card Component
import React from 'react';
import { Link } from "react-router-dom";
import { LazyLoadImage } from "react-lazy-load-image-component";
const FruitWidget = ({ fruit }) => {
return (
<div className="col-6">
<div className="card mb-2">
<Link to={"/fruit-details/"+fruit.id}>
<LazyLoadImage src="assets/img/sample/photo/product1.jpg"
className="card-img-top lazycolor" alt="image" />
<div className="card-body">
<h4 className="mb-0">{fruit.name}</h4>
</div>
</Link>
</div>
</div>
);
}
export default FruitWidget;
The Items List
import React, { useState, useEffect } from 'react';
import { useNavigate } from "react-router-dom";
import Menubar from "../../components/menubar/Menubar"
import FruitWidget from "../../components/widgets/FruitWidget"
import { LazyLoadImage } from "react-lazy-load-image-component";
import WidgetSkeleton from "../../components/skeleton/WidgetSkeleton"
function Training() {
let navigate = useNavigate();
const [fruits, setFruits] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetch("http://localhost:8000/api/fruits")
.then((result) => result.json())
.then((fruits) => {
setFruits(fruits);
setIsLoading(false);
});
}, []);
console.warn("result", fruits)
return (
<div className="section">
<div className="row">
{isLoading && <WidgetSkeleton cards={6} />}
{fruits.map((fruit) => (
<FruitWidget fruit={fruit} key={fruit.id} />
))}
</div>
</div>
);
}
export default Training;
The Details Component
import React, { useState, useEffect } from 'react';
import { useNavigate, useParams } from "react-router-dom";
import Menubar from "../../components/menubar/Menubar"
import Tabs from "../../components/fruittabs/FruitTabs";
function FruitDetails() {
let navigate = useNavigate();
const [fruit, setFruit] = useState({});
const { fruitId } = useParams();
useEffect(() => {
fetch(`http://localhost:8000/api/fruit/${fruitId}`)
.then((result) => result.json())
.then((fruit) => {
setFruit(fruit[0]);
});
}, [fruitId]);
return (
<div>
<Menubar />
<div className="appHeader bg-primary text-light">
<div className="left">
<a onClick={() => navigate(-1)} className="headerButton goBack">
<i className="fi fi-rr-angle-left"></i> </a>
</div>
<div className="pageTitle">{fruit.name}</div>
<div className="right"></div>
</div>
<div id="appCapsule">
<div className="section mt-3 mb-3">
<img src="assets/img/lazyload.svg"
alt="image" className="imaged img-fluid fruit-detail-main" />
</div>
<div className="section mt-3 mb-3">
<div>
<Tabs>
<div label="Details">
{fruit.details}
</div>
<div label="Infestations">
After 'while, <em>Crocodile</em>!
</div>
<div label="Advice">
Nothing to see here, this tab is <em>extinct</em>!
</div>
</Tabs>
</div>
</div>
</div>
</div>
);
}
export default FruitDetails;
Related
I am having a single fruit with a list of infestations that I'm mapping on my react component. I want to open an ActionSheet with the details of an individual infestation when it is clicked. I have tried this way but its not working. Anyone, please help.
import React, { useState, useEffect, useRef } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
import Menubar from "../../components/menubar/Menubar";
import Tabs from "../../components/fruittabs/FruitTabs";
import WidgetSkeleton from "../../components/skeleton/WidgetSkeleton"
import Skeleton from "react-loading-skeleton";
import toast, { Toaster } from 'react-hot-toast';
import axios from 'axios';
import InfestationWidget from "../../components/widgets/InfestationWidget"
import ActionSheet from "actionsheet-react";
import { LazyLoadImage } from "react-lazy-load-image-component";
function FruitDetails() {
let navigate = useNavigate();
const [fruit, setFruit] = useState({});
const { fruitId } = useParams();
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
let isMounted = true;
axios.get(`/api/fruit/${fruitId}`).then(res => {
if (isMounted) {
if (res.data.status === 200) {
setFruit(res.data.fruit);
setIsLoading(false);
}
else if (res.data.status === 404) {
toast.error(res.data.message, "error");
}
}
});
return () => {
isMounted = false
};
}, []);
const ref = useRef(fruit.infestation && fruit.infestation.infestationid);
const handleOpen = () => {
ref.current.open();
console.log(`Fruit Infestation id: ${fruit.infestation.infestationid}`);
};
const handleClose = () => {
ref.current.close();
}
return (
<div>
<Menubar />
<div className="appHeader bg-primary text-light">
<div className="left">
<a onClick={() => navigate(-1)} className="headerButton goBack">
<i className="fi fi-rr-angle-left"></i>{" "}
</a>
</div>
<div className="pageTitle">{fruit.name}</div>
<div className="right"></div>
</div>
<Toaster />
<div id="appCapsule">
<div className="section mt-3 mb-3">
{isLoading ?
<Skeleton height={150} /> :
<LazyLoadImage
effect="blur"
width={'100%'}
src={`${process.env.REACT_APP_API_URL}/storage/fruits/${fruit.image}`}
alt="image"
className="imaged img-fluid fruit-detail-main"
/>}
</div>
<div className="section mt-3 mb-3">
<div>
<Tabs>
<div label="Details">
{isLoading && (
<Skeleton height={25} count="8" className="mb-05" />
)}
<div
dangerouslySetInnerHTML={{
__html: fruit.description,
}}
/>
</div>
<div label="Infestations">
<div className="mb-1">Here are some of the Popular Infestations for {fruit.name}</div>
<h3 className="mb-1">All Infestations</h3>
<div className="row">
{isLoading && <WidgetSkeleton cards={6} />}
{fruit.infestation && fruit.infestation.map((infestation) => (
<div className="col-6" infestation={infestation} key={infestation.infestationid}>
<div className="card mb-2">
<a onClick={handleOpen}>
<LazyLoadImage
src={`${process.env.REACT_APP_API_URL}/storage/infestations/${infestation.infestationimage}`}
className="card-img-top" alt="image" />
<div className="card-body card-bodysmall">
<p className="mb-0 text-sm-x">{infestation.infestationtype}</p>
<h4 className="mb-0">{infestation.infestationname}</h4>
</div>
</a>
</div>
</div>
))}
</div>
</div>
<div label="Advice">
<div
dangerouslySetInnerHTML={{
__html: fruit.advice,
}}
/>
</div>
</Tabs>
<ActionSheet
ref={ref}
sheetStyle={{
borderTopLeftRadius: 15,
borderTopRightRadius: 15,
height: '80%'
}}>
<div className="bar" />
//Single Fruit Infestation Details
</ActionSheet>
</div>
</div>
</div>
</div>
);
}
export default FruitDetails;
Please help! I don't know why I'm getting this error. I can't find what I need to change :( The needed output in browser is perfectly fine. But I am getting this error. I'm not used to list and keys on react. I have the latest versions of React and Nodejs and the packages needed
Homescreen.js:
import React, { useState, useEffect } from 'react'
import axios from 'axios';
import Room from '../components/Room';
import Loader from '../components/Loader';
import Error from '../components/Error';
function Homescreen() {
let [rooms, setrooms] = useState([]);
const [loading, setloading] = useState();
const [error, seterror] = useState();
useEffect(() => {
async function getResults() {
try {
seterror(false);
setloading(true);
const data = (await axios('/api/rooms/getallrooms')).data;
setrooms(data);
setloading(false);
} catch (e) {
seterror(true);
setloading(false);
}
}
getResults();
}, []);
return (
<div className='container'>
<div className='row justify-content-center mt-5'>
{loading ? (
<Loader />
) : rooms.length > 1 ? (
rooms.map(room => {
return <div className="col-md-9 mt-3">
<Room room={room} />
</div>;
})
) : (
<Error />
)}
</div>
</div>
)
}
export default Homescreen;
//<h1>{room.name}</h1>
and my Room.js:
import React, { useState } from "react";
import { Modal, Button, Carousel } from 'react-bootstrap'
import { First } from "react-bootstrap/esm/PageItem";
import { Link } from 'react-router-dom'
function Room({ room }) {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
< div className="row bs" >
<div className="col-md-4">
<img src={room.imageurls[0]} className="smallimg" />
</div>
<div className="col-md-7">
<h1>{room.name}</h1>
<b>
{" "}
<p>Max Count : {room.maxcount}</p>
<p>Phone Number : {room.phonenumber}</p>
<p>Type : {room.type}</p>
</b>
<div style={{ float: "right" }}>
<Link to={`/book/${room._id}`}>
<button className="btn btn-primary m-5">Book Now!</button>
</Link>
<button className="btn btn-primary" onClick={handleShow}>View Details</button>
</div>
</div>
<Modal show={show} onHide={handleClose} size='lg'>
<Modal.Header>
<Modal.Title>{room.name}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Carousel prevLabel='' nextLabel=''>
{room.imageurls.map(url => {
return <Carousel.Item>
<img
className="d-block w-100 bigimg"
src={url}
/>
</Carousel.Item>
})}
</Carousel>
<p>{room.description}</p>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
export default Room;
Browser Console Error:
You have to give the first element in a map function a key:
rooms.map((room, index) => {
return (
<div key={index} className="col-md-9 mt-3">
<Room room={room} />
</div>
);
});
import React from 'react';
import './app.css';
import Inbox from "./inbox/inbox";
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Navbar from './nav/navbar';
import Sidebar from './sidebar/sidebar';
import Composer from './composer/composer';
function App(){
return (
<>
<Router>
<Navbar/>
<div className="main-page">
<Sidebar/>
<div className="Clicked-Item-Content">
<Routes>
<Route path='/inbox' element={<Inbox />} />
<Route path='/' element={<Composer />} />
</Routes>
</div>
</div>
</Router>
</>
)
}
export default App;
The button Link part of component
import { NavLink } from "react-router-dom";
import "./sidebar.css";
const Sidebar = () => {
return (
<div className="sidebar">
<ul>
<li>
<NavLink to ='/'>
<i className="fa-solid fa-square-plus box awesome"></i>
Composer
</NavLink>
</li>
<li>
<NavLink to ='/inbox'>
<i className="fa-solid fa-inbox box awesome"></i>
Inbox
</NavLink>
</li>
</ul>
</div>
)
}
export default Sidebar;
The Inbox component part is below when I click on this it doesn't show anything on screen screen is completely empty, and other link are working fine. My local storage is not empty. I stored an object with key value pair inside an array. When I try to map it doesn't show up on screen, it shows an empty screen.
import { useEffect, useState } from "react";
import "./inbox.css";
const Inbox = () => {
const [localStorageData, setlocalStorageData] = useState([])
useEffect(() => {
JSON.parse(localStorage.getItem('todos')).then(data=> setlocalStorageData(data))
console.log(localStorageData)
.catch(err=> console.log(err))
}, [localStorageData])
return (
<>
{localStorageData.length > 0 &&
localStorageData.map((element, index)=> (
<div className="inbox" key={index}>
<div className="bullet">➣</div>
<div className="msg">{element.message}</div>
<div className="btn-container">
<div className="date">{element.title}</div>
<div className="edit btn hide">
<i className="fa-solid fa-pen-to-square"></i>
</div>
<div className="delete btn hide">
<i className="fa-solid fa-trash"></i>
</div>
</div>
</div>
)
)}
</>
)
}
export default Inbox;
JSON.parse doesn't return a Promise. You can initialize the localStorageData state directly from localStorage. You also don't want to use any values as a React hook dependency that you are unconditionally enqueueing state updates for as this can lead to render looping if not properly guarded.
Example:
const Inbox = () => {
const [localStorageData, setlocalStorageData] = useState(
JSON.parse(localStorage.getItem('todos')) ?? []
);
...
return (
<>
{localStorageData.map((element, index) => (
<div className="inbox" key={index}>
<div className="bullet">➣</div>
<div className="msg">{element.message}</div>
<div className="btn-container">
<div className="date">{element.title}</div>
<div className="edit btn hide">
<i className="fa-solid fa-pen-to-square"></i>
</div>
<div className="delete btn hide">
<i className="fa-solid fa-trash"></i>
</div>
</div>
</div>)
)}
</>
);
};
The code is also using an array index as the React key on the mapped inbox messages. I strongly suggest adding a GUID to the data when it's created that could be used as the key so each inbox message has a static property that uniquely identifies it regardless of where it's rendered.
Here is my reactjs code I want to reverse my data and display on slient side but reverse() is not working what to do..? I don't understand why is this happing with my code..! can anyone help me to reverse my data and display frontend(client side) ....! please help
import React, { useEffect, useState } from "react";
import { getBlogs } from "../helper/coreapicalls";
import ImageHelper from "../helper/ImageHelper";
import { Link } from "react-router-dom";
import Footer from "../Footer/Footer";
const Blog = (blog) => {
const [blogs, setBlog] = useState([]);
const [error, seterror] = useState(false);
useEffect(() => {
loadBlog();
}, []);
const loadBlog = () => {
getBlogs().then((data) => {
if (data.error) {
seterror(data.error);
} else {
setBlog(data);
// console.log(data);
}
});
};
// console.log(blogs);
return (
<div>
<div className="container py-md-5 py-3">
<h1 className="blog_heading">ALL BLOGS</h1>
<div className="row">
{blogs.reverse().map((blog, index) => (
<div className="col-lg-4 col-md-6 col-12" key={index}>
<Link to={`/blog/${blog._id}`} className="Blog_card_link">
<div className="card my-2 shadow">
<ImageHelper blog={blog} />
<div className="card-body">
<h5 className="card-title">
{blog.title.substring(0, 75) + " ..."}
</h5>
<div
className="description"
dangerouslySetInnerHTML={{
__html: blog.description.substring(0, 100) + " ...",
}}
/>
<Link
type="btn"
to={`/blog/${blog._id}`}
className="forMore_btn "
>
For More
</Link>
</div>
</div>
</Link>
</div>
))}
</div>
</div>
<Footer />
</div>
);
};
export default Blog;
Here is image of Frontend ( client side )
enter image description here
Yep! Try reversing your data when you set the state: setBlog(data.reverse()));
Hope this doesn't sound like a stupid question, but all queries I've searched on here and Google ask about only showing the render once fetches/requests have completed.
I want my React app to only show the render once the render has completed, including the CSS. At the moment, in a fraction of a second, you can see the page being built - in under a split second, but still it's not a fluid flow for the UX. Is there a way to only load the page once the render (including the CSS) is all done? I don't want to do a setTimeout with a loading page as that is very clunky.
Many thanks in advance
Code below:
import React, { useEffect, useContext } from 'react';
import { NavLink } from 'react-router-dom';
import axios from 'axios';
import '../../styles/MleaveReqUpper.css';
// import '../../styles/leaveRequests.css';
import leftArrow from '../../img/general/leftArrow.svg';
import teamsGrad from '../../img/general/teamsGrad.png';
import returnBack from '../../img/general/returnBack.svg';
import cog from '../../img/general/cog.svg';
import checklist from '../../img/general/checklist.svg';
import { DataContext } from '../../contexts/DataContext';
import $ from 'jquery';
import requestsSelected from '../../img/mFooter/requestsSelected.svg';
const MLeaveReqUpperLinks = () => {
const { teamAllows, toggleTeamAllows } = useContext(DataContext);
const navBlue = () => {
$('.f3').attr('src', requestsSelected);
};
useEffect(() => {
axios.get('db.json').then();
});
// render
return (
<div className='leaveReqUpperContainer'>
<img className='teamGradientOut' src={teamsGrad} />
<NavLink to='/requests'>
<div
className='backGroup'
onClick={() => {
navBlue();
if (teamAllows) {
toggleTeamAllows(false);
}
}}
>
<img
className='returnBack'
src={returnBack}
alt='Back to My Requests'
/>
</div>
</NavLink>
<h3 className='TeamRequests'>Team Requests</h3>
<div className='iconsM'>
<NavLink to='team-allowances'>
<img
onClick={() => {
toggleTeamAllows();
}}
className={`checklist ${!!teamAllows ? 'iconSelected' : ''}`}
src={checklist}
alt='Allowances'
/>
</NavLink>
<img className='cog' src={cog} alt='Settings' />
</div>
<div className='teamsXbar'>
<div className='teamsInnerContainer'>
<div className='teamMenuHolder'>
<p className='teamName teamSel '>All Staff</p>
</div>
<div className='teamMenuHolder'>
<p className='teamName'>Brewery</p>
</div>
<div className='teamMenuHolder'>
<p className='teamName'>Sales</p>
</div>
<div className='teamMenuHolder'>
<p className='teamName'>Finance</p>
</div>
<div className='teamMenuHolder'>
<p className='teamName'>Operations</p>
</div>
<div className='teamMenuHolder'>
<p className='teamName'>Marketing</p>
</div>
</div>
</div>
</div>
);
};
export default MLeaveReqUpperLinks;
You can use a loading state variable
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
axios.get('db.json').then(res=>setIsLoading(false););
});
return isLoading ? null : <div>All your view</div>