Uncaught TypeError: Cannot read properties of undefined (reading 'image') BookingCar.js - javascript

APP.JS
import './App.css';
import {BrowserRouter as Router,Routes,Route,Navigate } from "react-router-dom";
import Home from "./pages/Home";
import Login from "./pages/Login";
import Register from "./pages/Register";
import BookingCar from "./pages/BookingCar";
import "antd/dist/antd.css"
function App() {
return (
<Router>
<Routes>
<Route path='/' element={<Home/>}/>
<Route path="/login" element={<Login/>}></Route>
<Route path="/register" element={<Register />}></Route>
<Route path="/booking/:id" element={<BookingCar />}></Route>
</Routes>
</Router>
);
}
export default App;
BookingCar.js
import React, {useState,useEffect} from "react";
import { useDispatch, useSelector } from "react-redux";
import { getAllcars } from "../redux/action/carsAction";
import { useParams } from 'react-router-dom';
import Spinner from "../components/Spinner";
import DefaultLayout from "../components/DefaultLayout";
import { Row, Col} from "antd";
export default function BookingCar({match}){
const { carid } = useParams();
const {cars} = useSelector(state => state.carsReducer)
const {loading} = useSelector(state => state.alertReducer)
const [car, setcar] = useState({})
const dispatch = useDispatch()
useEffect(() => {
dispatch(getAllcars())
if(cars.length>0){
setcar(cars.find(o=>o._id === carid))
}
}, [cars])
return(
<DefaultLayout>
{loading && (<Spinner/> )}
<Row>
<Col lg={10} sm={24} xs={24}>
<img alt=""src={car.image} className="carimg"/>
</Col>
</Row>
</DefaultLayout>
)
}
Home.js
import React, {useState,useEffect} from "react";
import { useDispatch, useSelector } from "react-redux";
import DefaultLayout from "../components/DefaultLayout";
import { getAllcars } from "../redux/action/carsAction";
import { Button, Row, Col} from "antd";
import {Link} from "react-router-dom";
import Spinner from "../components/Spinner";
export default function Home(){
const {cars} = useSelector(state => state.carsReducer)
const {loading} = useSelector(state => state.alertReducer)
const dispatch = useDispatch()
useEffect(() => {
dispatch(getAllcars())
}, [])
return(
<DefaultLayout>
{loading === true && (<Spinner/> )}
<Row justify="center" gutter={16} className="mt-5">
{cars.map(car=>{
return <Col lg={5} sm={24} xs={24}>
<div className="car p-2 bs1 ">
<img alt=""src={car.image} className="carimg"/>
<div className="car-content d-flex align-items-center justify-content-between">
<div>
<p>{car.name}</p>
<p>{car.rentPerHour} Rent Per Hour</p>
</div>
<div>
<button className="btn1 mt-2"><Link to={`/booking/${car._id}`}>Book now </Link></button>
</div>
</div>
</div>
</Col>
})}
</Row>
</DefaultLayout>
)
}
In BookingCar.js i am trying to get the car details like id (image)but i am getting error
So please help me how to solve this issue.

You're trying to access car before it's loaded/applied to state. Check if it exists first before trying to use it -
{car && (
<Row>
<Col lg={10} sm={24} xs={24}>
<img alt=""src={car.image} className="carimg"/>
</Col>
</Row>
)}

carid is undefined since it's not a defined route param (path="/booking/:id") so the .find function returns undefined.
You've valid car initial state
const [car, setcar] = useState({});
so you should be able to destructure (car.image & <img alt=""src={car.image} className="carimg"/>) from car without issue. The issue comes later when filtering cars by the cardid route param.
useEffect(() => {
dispatch(getAllcars());
if (cars.length > 0) {
setcar(cars.find(o => o._id === carid));
}
}, [cars]);
array.find can potentially return undefined if no match is found, so the UI should handle that. Route match params are also always strings, so if the _id fields are not also "string" type the strict equality won't work. Try doing a type-safe comparison by converting to strings.
cars.find(o => String(o._id) === carid)
...
return (
<DefaultLayout>
{loading && <Spinner />}
{car && (
<Row>
<Col lg={10} sm={24} xs={24}>
<img alt=""src={car.image} className="carimg" />
</Col>
</Row>
)}
</DefaultLayout>
);
Finally, you define the route match param as ":id" but destructure a carid in the component. Ensure the match params match.
If route is:
<Route path="/booking/:id" element={<BookingCar />} />
use const { id } = useParams();
otherwise, update the route param to match the code:
<Route path="/booking/:carid" element={<BookingCar />} />
use const { carid } = useParams();

Related

Why useEffect is not listening to changes?

I am having a react-quiz application whose code is:
Quiz.js file:
const Quiz = (props) => {
const [options,setOptions]=useState();
const [questions,setQuestions]=useState(props.questions);
const [currentQuestion,setCurrentQuestion]=useState(0);
useEffect(()=>{
console.log(questions);
var optionss=[];
optionss.push(questions[currentQuestion].correct_answer);
questions[currentQuestion].incorrect_answers.forEach((ans)=>optionss.push(ans));
optionss.sort(()=>Math.random()-0.5);
setOptions(optionss);
},[options])
return (
<div className='quiz'>
<span className="subtitle">Welcome ,{props.name}</span>
<div className="questionInfo">
<span>{questions[currentQuestion].category}</span>
<span>Score : {props.score}</span>
</div>
<Question
questions={questions}
setQuestions={setQuestions}
currentQuestion={currentQuestion}
setCurrentQuestion={setCurrentQuestion}
options={options}
correctOption={questions[currentQuestion].correct_answer}
score={props.score}
setScore={props.setScore}
/>
</div>
);
};
export default Quiz;
Question.js file:
const Question = ({
questions,
setQuestions,
currentQuestion,
setCurrentQuestion,
options,
correctOption,
score,
setScore
}) => {
useEffect(()=>{
},[]);
console.log(options);
<h1>Question : {currentQuestion+1}</h1>
<div className="singleQuestion">
<h2>{questions[currentQuestion].question}</h2>
<div className="options">
{options.map((option)=>{
return(
<button
disabled={selected}
key={option}
onClick={()=>{}}
className={`singleOption ${selected && handleSelect(option)}`}
>{option}</button>
)
})}
</div>
</div>
App.js file:
import { BrowserRouter,Routes,Route} from 'react-router-dom';
import './App.css';
import Quiz from './components/quiz/quiz';
import axios from "axios"
import {useState} from 'react'
import Home from './components/home/home';
import Header from './components/header.js';
import Result from './components/result/result';
import Footer from './components/footer';
import { useEffect } from 'react';
function App() {
const [name,setName]=useState("");
const [score,setScore]=useState(0);
const [questions,setQuestions]=useState();
useEffect(()=>{
console.log("Questions have changed");
},[questions]);
const fetchQuestions=async(category,difficulty)=>{
const {data}=await axios(`https://opentdb.com/api.php?amount=10&category=${category}&difficulty=${difficulty}&type=multiple`);
setQuestions(data.results);
}
return (
<BrowserRouter>
<div className="App" style={{backgroundImage: "url(./ques1.png)"}}>
<Header/>
<Routes>
<Route path="/home" exact element={<Home name={name} setName={setName} fetchQuestions={fetchQuestions}/>}></Route>
<Route path="/quiz" exact element={<Quiz name={name} questions={questions} score={score} setScore={setScore} />}></Route>
</Routes>
<Footer></Footer>
</div>
</BrowserRouter>
);
}
export default App;
Even though my Quiz component is able to get the questions from the App component,however when I send the question,options to the Question component as props from the Quiz component,I get undefined on logging in the Question component.
UseEffect in Quiz.js file has no dependencies: [], it will only run once, for first component rendering. Pass there the value that you want to watch for: [options]

React pass fetched data from API to another component

I am fetching few products from an API, and displaying them in card. There is a More Details link on the cards, where if the user clicks on it, it will take the user to the selected product details page. My routing to productDetails page works, But I am having troubles to find a way to pass the fetched data to the productDetails page as props.
This is what I have so far:
My FeaturedProduct.js:
import React from "react";
import { useState, useEffect } from "react";
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import ProductDetails from "./ProductDetails";
import axios from "axios";
function FeaturedProduct(props) {
const [products, setProducts] = useState([]);
useEffect(() => {
fetchProducts();
}, []);
function fetchProducts() {
axios
.get("https://shoppingapiacme.herokuapp.com/shopping")
.then((res) => {
console.log(res);
setProducts(res.data);
})
.catch((err) => {
console.log(err);
});
}
return (
<div>
<h1> Your Products List is shown below:</h1>
<div className="item-container">
{products.map((product) => (
<div className="card" key={product.id}>
{" "}
<h3>{product.item}</h3>
<p>
{product.city}, {product.state}
</p>
<Router>
<Link to="/productdetails">More Details</Link>
<Switch>
<Route path="/productdetails" component={ProductDetails} />
</Switch>
</Router>
</div>
))}
</div>
</div>
);
}
export default FeaturedProduct;
My Product Details Page:
import React from "react";
import FeaturedProduct from "./FeaturedProduct";
function ProductDetails(props) {
return (
<div>
<div>
<h1>{props.name}</h1>
<h1>{props.color}</h1>
</div>
</div>
);
}
export default ProductDetails;
I am still learning but this is what I would do:
<Route path="/productdetails">
<ProductDetails product={product}/>
</Route>
====
On ProductDetails you can destructure the props:
function ProductDetails(props) {
const {name, color} = props.product;
return (
<div>
<div>
<h1>{name}</h1>
<h1>{color}</h1>
</div>
</div>
);
}
export default ProductDetails;
Pass it as an element with props, if you are using v 6; sorry I didn't ask which version. >
<Switch>
<Route path="/productdetails" element={<ProductDetails {...props} />}/>
</Switch>
if version v4/5 use the render method >
<Route path="/productdetails" render={(props) => (
{ <ProductDetails {...props} />} )}/>
//pass it this way
<Switch>
<Route
path="/productdetails"
render={() => (
{ <ProductDetails product={product}/>})}/>
/>
</Switch>

Unable to redirect on button click React.js (using context provider)

I'm new to React and I've been trying to redirect to a different component after getting a response from my API.
I've tried using history, location, and Redirect, but the redirect never happens.
Also, I get undefined when using all of the above.
I'm not sure if this is because my App is defined outside the Router, if it is the reason I'm still unable to fix the issue.
Here is my code:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { AppProvider } from './Context'
import { BrowserRouter as Router } from 'react-router-dom'
ReactDOM.render(
<React.StrictMode>
<AppProvider>
<App />
</AppProvider>
</React.StrictMode>,
document.getElementById('root')
);
App.js
import React from 'react';
import './App.css';
import {Home, JoinRoom, CreateRoom, Room } from './pages';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route path="/" exact={true}>
<Home />
</Route>
<Route path="/join">
<JoinRoom />
</Route>
<Route path="/create">
<CreateRoom />
</Route>
<Route path="/room/:roomCode">
<Room />
</Route>
</Switch>
</Router>
</div>
);
}
export default App;
Context.js
Here, in the handleRoomButtonPressed, I'm getting data from the API and trying to redirect.
import React, { useState, useContext } from 'react';
import axios from 'axios';
import { Redirect, useHistory } from "react-router-dom";
const AppContext = React.createContext()
const AppProvider = ({ children }) => {
// const history = useHistory();
const [guestCanPause, setGuestCanPause] = useState(true);
const [votesToSkip, setVotesToSkip] = useState(2);
const [isHost, setIsHost] = useState(false);
const handleVotesChange = (e) => {
e.preventDefault();
setVotesToSkip(e.target.value);
}
const handleGuestCanPauseChange = (e) => {
e.preventDefault();
setGuestCanPause(e.target.value)
}
const handleRoomButtonPressed = async (props) => {
const roomData = { guest_can_pause: guestCanPause, votes_to_skip: votesToSkip };
const response = await axios.post('/api/create-room/', roomData);
console.log(response.data)
const redirectUrl = "/room/" + response.data.code;
console.log(props)
return <Redirect to={redirectUrl} />
}
const getRoomDetails = async (roomCode) => {
axios
.get("/api/get-room?code=" + roomCode)
.then((res) => {
console.log(res.data)
setVotesToSkip(res.data.votes_to_skip);
setGuestCanPause(res.data.guest_can_pause);
setIsHost(res.data.is_host);
})
.catch((err) => console.log(err));
}
return <AppContext.Provider value={{ guestCanPause,
votesToSkip,
isHost,
handleGuestCanPauseChange,
handleVotesChange,
handleRoomButtonPressed,
getRoomDetails, }}>
{children}
</AppContext.Provider>
}
export const useGlobalContext = () => {
return useContext(AppContext)
}
export { AppContext, AppProvider }
The onClick is called in CreateRoom.js
import React, { useState, } from 'react';
import { useGlobalContext } from '../Context'
import { Link } from 'react-router-dom';
import { Button, Grid, Typography, TextField, FormHelperText, FormControl, Radio, RadioGroup, FormControlLabel } from '#material-ui/core'
function CreateRoom() {
const defaultVotes = 2;
const { handleGuestCanPauseChange, handleVotesChange, handleRoomButtonPressed } = useGlobalContext();
return (
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography component="h4" variant="h4">
Create A Room
</Typography>
</Grid>
<Grid item xs={12} align="center">
<FormControl component="fieldset">
<FormHelperText>
<div align="center">Guest Control of Playback state</div>
</FormHelperText>
<RadioGroup row defaultValue="true" onChange={handleGuestCanPauseChange}>
<FormControlLabel value="true"
control={<Radio color="primary" />}
label="Play/Pause" labelPlacemment="bottom" />
<FormControlLabel value="false"
control={<Radio color="secondary" />}
label="No Control" labelPlacemment="bottom" />
</RadioGroup>
</FormControl>
</Grid>
<Grid item xs={12} align="center">
<FormControl>
<TextField required={true}
type="number" onChange={handleVotesChange}
defaultValue={defaultVotes}
inputProps={{ min: 1,
style: { textAlign: "center" },
}}
/>
<FormHelperText>
<div align="center">Votes Required To Skip Song</div>
</FormHelperText>
</FormControl>
</Grid>
<Grid item xs={12} align="center">
<Button
color="primary"
variant="contained"
onClick={handleRoomButtonPressed}
>
Create A Room
</Button>
</Grid>
<Grid item xs={12} align="center">
<Button color="secondary" variant="contained" to="/" component={Link}>
Back
</Button>
</Grid>
</Grid>
)
}
export default CreateRoom
If I understood a subject correctly your AppProvider is located above the router in the component tree. Thus, the react router cannot inject its dependencies into your AppProvider. If you want to access react-router API, such as useHistory hook or others, you should call it from one of the Router children, then it will works.

React Mulitple rendering functional component

hy i begin in react and i try to get user data from my api with my cookie, i have the data but when i console.log() the user data i have 4 undefined and then 2 same object data. The problem is i don't know why and when i try to get user.id in an other components using props i have tpyerror id undefined.
import React, {useState, useEffect} from "react";
import NavBar from "./component/NavBar";
import Navigation from "./component/Navigation"
import Cookie from "js-cookie"
import axios from "axios"
import "./App.css";
function App() {
const [user, setUser] = useState()
const [logged, setLogged] = useState(false)
const cookie = Cookie.get("login")
useEffect(() => {
axios.post('/getdatafromcookie', cookie)
.then((res) => {
if(res.data.success === true){
setLogged(true)
setUser(res.data.user)
}
})
}, [])
console.log(user)
return (
<div className="App">
<header className="NavHeader">
<NavBar user={user} logged={logged} />
</header>
<Navigation user={user} logged={logged}/>
</div>
);
}
export default App;
And the console log shows me :
Undefined
Undefined
Undefined
Undefined
{id: "31", email:"test#test.fr" etc.....}
{id: "31", email:"test#test.fr" etc.....}
Navbar
import React, { useState, useEffect } from "react";
import RegisterForm from "../component/RegisterForm";
import RegisterLogin from "../component/RegisterLogin";
import { Navbar, Nav, Dropdown } from "react-bootstrap";
import { Button } from "#material-ui/core"
export default function NavBar(props) {
const [modalLoginShow, setModalLoginShow] = useState();
const [modalRegisterShow, setModalRegisterShow] = useState();
const user = props.user
const islogged = props.logged
if(islogged === false)
{
return (
<Navbar collapseOnSelect expand="lg" bg="transparent" variant="light">
<Navbar.Brand href="/">Matchandate</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto"></Nav>
<Nav>
<Nav.Link onClick={() => setModalLoginShow(true)}>
<Button variant="contained" color="secondary">Login</Button>
</Nav.Link>
<Nav.Link onClick={() => setModalRegisterShow(true)}>
<Button variant="contained" color="secondary" >Register</Button>
</Nav.Link>
</Nav>
</Navbar.Collapse>
<RegisterLogin show={modalLoginShow} onHide={() => setModalLoginShow(false)} />
<RegisterForm show={modalRegisterShow} onHide={() => setModalRegisterShow(false)} />
</Navbar>
)
}
return(
<Navbar collapseOnSelect expand="lg" bg="transparent" variant="light">
<Navbar.Brand href="/">Matchandate</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto"></Nav>
<Nav>
<Nav.Link href="/profile">
<Button variant="contained" color="primary">Profile</Button>
</Nav.Link>
<Nav.Link href="/logout">
<Button variant="contained" color="primary" >Logout</Button>
</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
)
}
Navigation
import React from "react"
import Slider from "./SliderHome";
import Activate from "./Activate";
import ForgotPwd from "./Pages/ForgotPwd"
import ChangePwd from "./Pages/ChangePwd"
import UserProfile from "./Pages/UserProfil";
import ErrorPage from "./Pages/ErrorPage"
import { BrowserRouter as Router, Route, Switch, Redirect } from "react-router-dom";
export default function Navigation(props){
const user = props.user
const islogged = props.logged
if(islogged){
return(
<Router>
<Switch>
<Route exact path="/" exact component={Slider} />
<Route exact path="/profile" component={() => <UserProfile user={user} />} />
{/* <Route path="/user/:id" component={ChangePwd}/> */}
<Route path="/" component={ErrorPage} />
</Switch>
</Router>
)
}
return (
<Router>
<Switch>
<Route exact path="/" exact component={Slider} />
<Route path="/activate" component={Activate} />
<Route path="/forgot-pwd" component={ForgotPwd}/>
<Route path="/changepwd" component={ChangePwd}/>
{/* <Route path="/user/:id" component={ChangePwd}/> */}
<Route path="/" component={ErrorPage} />
</Switch>
</Router>
)
}
The problem is i don't know why and when i try to get user.id in an other components using props i have tpyerror id undefined.
The reason for that is that you try to get the id of the user before the user is loaded, which means you're doing something like null.id which is undefined (depends on what useState returns when called without any arguments, probably null)
The reason you get multiple Undefined in console.log is:
the first time your App component is rendered, useEffect didn't finish calling the api yet, so user is still undefined, the 2nd time is because you called setLogged(true) so user is still undefined, the 3rd and 4th times ... i'm not sure, but you're probably changing the state somehow which causes a re-render
the proper wait to fix this, would be to wait until user is defined (ie the api call is finished), you can do that by using a simple if statement, something like
if (user.id) {
// return components when the user is logged in
} else {
// return components where the user is not logged in, usually a "loading" screen
}
Now you said user.id returns type error but i couldn't find any user.id in your code, so i assumed that you didn't post the whole thing.
This happens because useEffect is executed after the first render, so, the first time user is null, you will need to guard your code to render once you have data inside user
return user ? <div className=“app”><NavBar user={user}/></div> : <div>loading</div>)
The logs being printed so many times is because strict mode in development
In yours first render: your user is undefined it's normal because your are not defined a default value in your usestate you can change your code by this const [user, setUser] = useState({}) or this
import React, {useState, useEffect} from "react";
import NavBar from "./component/NavBar";
import Navigation from "./component/Navigation"
import Cookie from "js-cookie"
import axios from "axios"
import "./App.css";
function App() {
const [user, setUser] = useState()
const [logged, setLogged] = useState(false)
const cookie = Cookie.get("login")
useEffect(() => {
axios.post('/getdatafromcookie', cookie)
.then((res) => {
if(res.data.success === true){
setLogged(true)
setUser(res.data.user)
}
})
}, [])
console.log(user)
return (
<div className="App">
<header className="NavHeader">
{user ? <NavBar user={user} logged={logged} /> : "loading"}
</header>
{user ? <Navigation user={user} logged={logged}: "loading" />
</div>
);
}
export default App;

Element type is invalid: expected a string (for built-in components) ...... Check the render method of `App`

LINK TO PHOTO OF FILE TREE
PHOTO OF AUTH0 APP AFTER CODE FIXES
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of App.
Why are I getting this error?
index.js
import ReactDOM from 'react-dom';
import { makeMainRoutes } from './routes';
const routes = makeMainRoutes();
ReactDOM.render(
routes,
document.getElementById('root')
);
routes.js
import React from 'react';
import { Route, Router } from 'react-router-dom';
import App from './App';
import Home from './Home/Home';
import Callback from './Callback/Callback';
import Auth from './Auth/Auth';
import history from './history';
const auth = new Auth();
const handleAuthentication = (nextState, replace) => {
if (/access_token|id_token|error/.test(nextState.location.hash)) {
auth.handleAuthentication();
}
}
export const makeMainRoutes = () => {
return (
<Router history={history} component={App}>
<div>
<Route path="/" render={(props) => <App auth={auth} {...props} />} />
<Route path="/home" render={(props) => <Home auth={auth} {...props} />} />
<Route path="/callback" render={(props) => {
handleAuthentication(props);
return <Callback {...props} />
}}/>
</div>
</Router>
);
}
App.js
import React, { Component } from 'react';
import { Navbar, Button } from 'react-bootstrap';
import './App.css';
class App extends Component {
goTo(route) {
this.props.history.replace(`/${route}`)
}
login() {
this.props.auth.login();
}
logout() {
this.props.auth.logout();
}
componentDidMount() {
const { renewSession } = this.props.auth;
if (localStorage.getItem('isLoggedIn') === 'true') {
renewSession();
}
}
render() {
const { isAuthenticated } = this.props.auth;
return (
<div>
{/* <script type="text/javascript" src="node_modules/auth0-js/build/auth0.js"></script> */}
{/* <script src="https://cdn.auth0.com/js/auth0/9.10/auth0.min.js"></script> */}
<Navbar fluid>
<Navbar.Header>
<Navbar.Brand>
Auth0 - React
</Navbar.Brand>
<Button
bsStyle="primary"
className="btn-margin"
onClick={this.goTo.bind(this, 'home')}
>
Home
</Button>
{
!isAuthenticated() && (
<Button
id="qsLoginBtn"
bsStyle="primary"
className="btn-margin"
onClick={this.login.bind(this)}
>
Log In
</Button>
)
}
{
isAuthenticated() && (
<Button
id="qsLogoutBtn"
bsStyle="primary"
className="btn-margin"
onClick={this.logout.bind(this)}
>
Log Out
</Button>
)
}
</Navbar.Header>
</Navbar>
</div>
);
}
}
export default App;
undefined && whatever resolves to undefined. Check the value returned by this.props.auth.isAuthenticated(). It should be boolean. You can use !! to fix the issue:
{
!!isAuthenticated() && (
<Button
id="qsLogoutBtn"
bsStyle="primary"
className="btn-margin"
onClick={this.logout.bind(this)}
>
Log Out
</Button>
)
}
But since you have to alternatives for this conditional it is better just to use this code:
{
isAuthenticated() ? (
<Button
id="qsLogoutBtn"
bsStyle="primary"
className="btn-margin"
onClick={this.logout.bind(this)}
>
Log Out
</Button>
) : (
<Button
id="qsLoginBtn"
bsStyle="primary"
className="btn-margin"
onClick={this.login.bind(this)}
>
Log In
</Button>
)
}

Categories