I have a react application that renders from a main component, when this component is mounted I fetch some data using redux thunk and pass the response as a global state, in this main component there is a component that render a specific item of the array of data that redux passed as props, for this I filter the array data with the id parameter in the url that match, the problem is that when the page is reloaded this data is no longer available so the view of the specific item crash, so I am trying to create a ternary operator condition that check if the props array length is 0 then an axios request is done to the api to get the specific item using the parameter of the url but though I see the response in the console this does not pass as prop to the component that render the specific item just the promise is seen.
the main component is this:
import React, { Component } from 'react';
import {Routes, Route, Navigate, useParams, useNavigate} from 'react-router-dom'; //Switch changed to routes Also redirect is changed to Navigate since version 6
import {connect} from 'react-redux';
import { useLocation } from 'react-router-dom';
import Header from './HeaderComponent';
import Home from './home-components/HomeComponent';
import Footer from './FooterComponent';
import Vitrina from './vitrina-components/VitrinaComponent';
import CarDetailComponent from './vitrina-components/CardetailComponent';
import {fetchCars, fetchOfertas} from '../redux/ActionCreators';
import axios from 'axios';
import { baseUrl } from '../shared/baseUrl';
// --------Hook to use withRouter from v5 in actual v6-----------------
export const withRouter = (Component) => {
const Wrapper = (props) => {
const history = useNavigate();
const location = useLocation();
const params = useParams();
return (
<Component
history={history}
location={location}
params={params}
{...props}
/>
);
};
return Wrapper;
};
const mapStateToProps = (state) => {
return{
cars: state.cars
}
}
const mapDispatchToProps = dispatch => ({
//axios fetch data function
fetchCars: () => { dispatch(fetchCars())}
});
class Main extends Component {
componentDidMount() {
//when mounted the data is fetched
this.props.fetchCars();
}
render(){
//this is the component where the specific item is rendered
const CarWithId = () => {
let params = useParams();
return(
// here is the actual logic to pass the specific item to the component to render
<CarDetailComponent car={this.props.cars.cars.filter((car) => car._id === params.carId)[0]}/>
);
};
return (
<div>
<Header/>
<Routes>
<Route path = "/login" element = {<LoginComponent/>}/>
<Route path="/home" element={<Home cars={this.props.cars}/>}/>
<Route exact path="/vitrina" element= {<Vitrina cars={this.props.cars} />} />
<Route path = "/vitrina/:carId" element={<CarWithId />} />
<Route path="*"element={<Navigate to="/home" />} />
{/* Instead of redirect the above is needed to redirect if there is no matched url*/}
</Routes>
<Footer location={this.props.location}/>
</div>
);
}
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Main));
what I am trying right now in the CarWithId to pass the specific item when the page is reloaded is this
const CarWithId = () => {
let params = useParams();
async function getCar(){
let car;
await axios.get(baseUrl 'cars/' params.carId)
.then((response) => {
car = response.data
console.log('response.data',response.data)
})
.catch((error) => {
console.log(error)
})
return car;
}
return(
<CarDetailComponent car={this.props.cars.cars.length===0 ? getCar() :this.props.cars.cars.filter((car) => car._id === params.carId)[0]}/>
);
};
but i get this in the console when login the getCar() function
how can i correctly do the axios request in order to pass the response.data as the car to the carDetailComponent, also in the inner console.log('response.data',response.data)
of the function the response is as what i expect but for a reason in the car this is not passed as the prop
EDIT added the carDetailComponent
import React, {useState, useEffect} from 'react';
import {
//Card, CardImg, CardText, CardBody, CardTitle,
Breadcrumb,BreadcrumbItem} from 'reactstrap';
import {Link} from 'react-router-dom'
import { baseUrl } from '../../shared/baseUrl';
import { formatMoney } from '../../shared/utils';
import whatsappIcon from '../../static/assets/icons/whatsapp.svg';
const imgPath = baseUrl "images/vehiculos/";
function RenderCar({car}){
// hooks to manage the state of the component
if (car != null){
const message = `Hola, estoy interesado en el vehículo ${car.marca} ${car.linea} ${car.modelo}`;
return(
<div className="container">
<div className="row mb-2">
{/* < -- Col -- > */}
<div className="col-12 col-md-9">
<div className='container-fluid '>
<div className='row'>
{/* < -- Col -- > */}
<div className='col-2'>
{/* here will be showed the miniatures in vertical when one is clicked will have a blue border*/}
{car.images.map((image, index) => {
return(
<div key={index} className='row vertical-center'>
{/* images must be responsive */}
<img src={imgPath
car.marca "_" car.linea "_" car.modelo "/" image}
alt={car.marca "_" car.linea "_" car.modelo "_" index}
className= {`card side-column-images px-0 mb-2 ${currentImage === index ? "selected" : undefined}`}
onClick={() => setCurrentImage(index)}
onm ouseOver={() => setCurrentImage(index)}
/>
</div>
);
})}
</div>
{/* < -- Col -- > */}
<div className='col-10'>
<img
className="card-img-top principal-image-vitrina mx-2"
src={imgPath car.marca "_" car.linea "_" car.modelo "/" car.images[currentImage]}
alt={car.name}
/>
</div>
</div>
</div>
</div>
{/* < -- Col -- > */}
{/*car information Marca, linea, modelo, precio*/}
<div className="col-3 col-md-3 card pb-2">
<div className="card-body">
<p className="card-text text-muted responsive-text">{car.modelo} | {car.km} km</p>
<h3 className="card-title responsive-title">{car.marca} {car.linea}</h3>
<h2 className="card-text display-6">${formatMoney(car.price)}</h2>
{/* buttons for buy and other to contact via whatsapp */}
<div className="row">
<div className="col-12 col-md-6">
<button type="button" className="btn btn-primary btn-block">Comprar</button>
</div>
{/* use whatsapp.svg icon that is on static/assets/icons*/}
<div className="col-12 col-md-6">
{/* button will redirect to wppApi onClick*/}
<button type="button" className="btn btn-success btn-block" onClick={() => window.open(wppApi, "_blank")}>
<img src={whatsappIcon} alt="whatsapp" style={{height:20, width:20,fill:"white"}} className="whatsapp-icon"/>
Contactar
</button>
</div>
</div>
</div>
</div>
</div>
{/* principal characterist rendered in a table, modelo, marca, linea, Tipo, km, cilindrake,transmision,direccion,combustible,color*/}
<div className="row">
<div className="col-12 col-md-9">
<div className="">
<div className="card-body">
<h4 className="card-title">Caracteristicas Principales</h4>
<table className="table table-striped">
<tbody>
<tr>
<th scope="row">Modelo</th>
<td>{car.modelo}</td>
</tr>
<tr>
<th scope="row">Marca</th>
<td>{car.marca}</td>
</tr>
<tr>
<th scope="row">Linea</th>
<td>{car.linea}</td>
</tr>
<tr>
<th scope="row">Tipo</th>
<td>{car.Tipo}</td>
</tr>
<tr>
<th scope="row">Kilometraje</th>
<td>{car.km}</td>
</tr>
<tr>
<th scope="row">Cilindraje</th>
<td>{car.cilindraje}</td>
</tr>
<tr>
<th scope="row">Transmision</th>
<td>{car.transmision}</td>
</tr>
<tr>
<th scope="row">Direccion</th>
<td>{car.direccion}</td>
</tr>
<tr>
<th scope="row">Combustible</th>
<td>{car.combustible}</td>
</tr>
<tr>
<th scope="row">Color</th>
<td>{car.color}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
);
}
else{
return(
// redirects user to /vitrina route
<div>
<h3>No se encontro el carro</h3>
</div>
);
}
}
const CarDetailComponents = (props)=> {
return (
<div className="container">
<div className="row">
<Breadcrumb>
<BreadcrumbItem><Link to="/vitrina">vitrina</Link></BreadcrumbItem>
<BreadcrumbItem active>{props.car.marca} {props.car.linea}</BreadcrumbItem>
</Breadcrumb>
<div className="col-12">
<h3>{props.car.marca} {props.car.linea}</h3>
<hr/>
</div>
</div>
<div className="row">
<RenderCar car={props.car}/>
</div>
</div>
);
}
export default CarDetailComponents;
CodePudding user response:
Remove the
getCar
function from yourCarWithId
componentYou can leave the render of the car details component like this
<CarDetailComponent car={this.props.cars.cars.length===0 ? null :this.props.cars.cars.filter((car) => car._id === params.carId)[0]}/>
- Then update your CarDetailComponents to this
const CarDetailComponents = (props)=> {
const [car, setCar] = useState(props.car)
let params = useParams();
async function getCar(){
await axios.get(baseUrl 'cars/' params.carId)
.then((response) => {
setCar(response.data)
console.log('response.data',response.data)
})
.catch((error) => {
console.log(error)
})
}
useEffect(() => {
if(!props.car){
getCar()
}
},[props.car])
return (
<div className="container">
<div className="row">
<Breadcrumb>
<BreadcrumbItem><Link to="/vitrina">vitrina</Link></BreadcrumbItem>
<BreadcrumbItem active>{car?.marca} {car?.linea}</BreadcrumbItem>
</Breadcrumb>
<div className="col-12">
<h3>{car?.marca} {car?.linea}</h3>
<hr/>
</div>
</div>
<div className="row">
{car && <RenderCar car={car}/> }
</div>
</div>
);
}
This will check if car is assigned, if not it will fetch the car
CodePudding user response:
why you also using await and then
how about ?
async function getCar() {
try {
const response = await axios.get(baseUrl 'cars/' params.carId);
console.log("response.data", response.data)
return response.data;
} catch (err) {
console.error(err)
}
}
useEffect(() => {
(async () => {
const car = await getCar()
setCar(car)
})();
}, [])
<CarDetailComponent car={car} />