I just tried to follow this question with no results for me.
The point is that I'm trying to get a selected pokemon from a component and pass down to another component. The data is correctly passed, some data are displayed, but with other (for example, the sprites) I got an error. I'm using Context by the way.
PokemonContext.js
import React, { useState } from 'react';
const Context = React.createContext({});
export function PokemonsContextProvider({ children }) {
const [favorites, setFavorites] = useState([]);
return <Context.Provider value={{ favorites, setFavorites }}>{children}</Context.Provider>;
}
export default Context;
App.js
<PokemonsContextProvider>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/pokemons" element={<PokemonsList />} />
<Route path="/favorites" element={<Favorite />} />
<Route path="/pokemon/:name" element={<PokemonDetails />} />
<Route path="*" element={<NotFound />} />
</Routes>
</PokemonsContextProvider>
PokemonsDetails.js
const PokemonDetails = () => {
const { name } = useParams();
const [pokemon, setPokemon] = useState('');
useEffect(() => {
pokemonService.pokemonDetail(name).then((res) => {
setPokemon(res);
});
}, []);
return (
<div>
<h1 className="text-center mb-4">Details for {name.toUpperCase()}</h1>
<PokemonCard pokemon={pokemon} />
<div className="text-center">
<Link className="btn btn-success mx-2 my-4" to="/favorites">
Go back
</Link>
</div>
</div>
);
};
PokemonCard.js
const PokemonCard = ({ pokemon }) => {
return (
<div className="card text-center bg-primary">
<div className="card-body">
<h5 className="card-title">{pokemon.name}</h5>
</div>
<ul className="list-group list-group-flush">
<li className="list-group-item">Weight: {pokemon.weight}</li>
<li className="list-group-item">Height: {pokemon.height}</li>
</ul>
</div>
);
};
export default PokemonCard;
When I add this line <img className="card-img-top" src={pokemon.sprites.front_default} alt="Card image cap" /> to the PokemonCard component I got this error:
The data is correctly displayed when I console log it, so, I don't know why I'm having this error. If I inspect the React Dev Tools the data is in the component!
The PokemonCard.js component is being rendered before the result of the request.
You can resolve this problem checking if there is a pokemon before use his props.
const PokemonCard = ({ pokemon }) => {
return pokemon && (
<div className="card text-center bg-primary">
<img className="card-img-top" src={pokemon.sprites?.front_default} alt="Card image cap" />
<div className="card-body">
<h5 className="card-title">{pokemon.name}</h5>
</div>
<ul className="list-group list-group-flush">
<li className="list-group-item">Weight: {pokemon.weight}</li>
<li className="list-group-item">Height: {pokemon.height}</li>
</ul>
</div>
);
};
export default PokemonCard;
Related
Onclick redirect with PARAMS in react is not working in Netlify
Hi everyone, I'm struck in a problem which works fine with localhost but creates issue with netlify.
I want to pass data onclick to a react js page via params, this works file on local host but give 404 when I tried to host this app on netlify.
Please Visit my site
As you can see on second page when I click on play video, it gives me 404.
For your clarity I'll provide my code to you.
App.js
function App() {
const style1 = {
display: "flex",
minHeight: "100vh",
flexDirection: "column",
};
return (
<div className="App">
<div style={style1}>
<HeaderComp />
<Routes>
<Route exact path="/" element={<FirstPage />} />
<Route exact path="/SecondPage" element={<SecondPage />} />
<Route
path="/ThirdPage/:url/:one/:two/:three/:four"
element={<ThirdPage />}
/>
<Route exact path="/About" element={<About />} />
<Route path="*" element={<FirstPage />} />
</Routes>
</div>
<FooterComp />
</div>
);
}
export default App;
Params giving Component
import React from "react";
// import { Link } from "react-router-dom";
export default function VideoCard({ data }) {
const style1 = {
width: "18rem",
};
return (
<div className="m-3 ">
<div className="card" style={style1}>
<img src={data.imageUrl} className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title">{data.videoTitle}</h5>
<p className="card-text">{data.videoDescription}</p>
<a
href={"/ThirdPage/" + data.videoUrl.substr(26)}
className="btn btn-primary"
>
Play Video
</a>
</div>
</div>
</div>
);
}
Params Recieving Component
import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
export default function ThirdPage() {
const params = useParams();
const [url, updateUrl] = useState("");
useEffect(() => {
const url1 =
"http://res.cloudinary.com" +
`/${params.url}/${params.one}/${params.two}/${params.three}/${params.four}`;
updateUrl(url1);
}, [url, params]);
return (
<div className="container mb-5">
{url && (
<div className="row">
<h1>This is Third page</h1>
<video width="750" height="500" controls loop autoPlay>
<source src={url} type="video/mp4" />
</video>
</div>
)}
</div>
);
}
The main problem is that this image is selected from the file explorer and I am using react-router.
Add.js
this is where you select the image
import { Link } from "react-router-dom";
import About from "./About";
import './styles/modal.css'
import firebase from "firebase";
require("firebase/firestore")
require("firebase/storage")
export default function Add(props) {
const [image, setImage] = useState(null);
const [modal, setModal] = useState(false);
const pickImage = (event) => {
//console.log();
setImage(URL.createObjectURL(event.target.files[0]));
//console.log(image);
}
const toggle = () => {
setModal(!modal);
}
return (
<div>
<h1>Add</h1>
<button onClick={toggle}>Add image</button>
{modal ? (
<div className="modal-bg">
<div className="modal">
<img src={image} style={{ width: '60%', height: '60%' }} className="ex-img" />
<br /><br />
<label htmlFor="fie" className="gcoo btn-default">+ File
<input id="fie" type="file" onChange={pickImage} className="file-pick" />
</label>
<br />
<br />
<div className="bottom-buttons">
<Link to="/about">
<button className="save">Save</button>
</Link>
<button onClick={() => setModal(false)} className="closse">Close</button>
</div>
</div>
</div>
) : null}
</div>
)
}
I am using firebase but not in this file so you can ignore this.
MainRoutes.js
this is where all the routes and pages are.
import { Route } from "react-router";
import { Switch } from 'react-router-dom';
import ImageDisplay from "./components/ImageDisplay";
import Add from './components/Add';
export default function MainRoute(props) {
return (
<Switch>
<Route exact path="/about" component={() => <ImageDisplay />} />
<Route exact path="/add" component={() => <Add />} />
</Switch>
);
}
finally this file ImageDisplay.js is where the image should be displayed
I dont have much on this file because i dont know how to put in any images.
I have tried props but whenever i imported the Imagedisplay it always showed the content on it and i dont want anything from image display. I only want to send an image over there.
import React from 'react'
import { Link } from "react-router-dom"
function ImageDisplay(props) {
return (
<div>
<h1>image</h1>
<div>
<img />
<p></p>
</div>
</div>
)
}
export default ImageDisplay;
Make sure that you are not reloading the page when moving between the two routes, since this will remove all state.
To share state between the two components you would need to store the state in the parent, and pass it to the children.
Add:
export default function Add(props) {
const pickImage = (event) => {
props.setImage(URL.createObjectURL(event.target.files[0]));
}
// The rest of your function
}
And in your parent:
export default function MainRoute(props) {
const [image, setImage] = useState(null)
return (
<Switch>
<Route exact path="/about" component={() => <ImageDisplay image={image} />} />
<Route exact path="/add" component={() => <Add setImage={setImage} />} />
</Switch>
);
}
You can now access the image prop in your imageDisplay.
function ImageDisplay(props) {
return (
<div>
<h1>image</h1>
<div>
<img src={props.image} />
<p></p>
</div>
</div>
)
}
I have Menu component. I'm adding items to Cart component by clicking on Add to Cart button.
I also have a Cart icon which holds array of items in Cart.
const Menu = () => {
const [cart, setCart] = useState([]);
const addToCart = (el) => setCart( [...cart, el]);
console.log(cart);
return (
<>
<Tabs className="tabs-wrapper" id="menu">
<TabList className="tabs">
<Tab className="tab-item">Burgers</Tab>
<Tab className="tab-item">Lunch of the day</Tab>
<Tab className="tab-item">Crepes</Tab>
</TabList>
<TabPanel>
<div className="burgers">
<ul>
{burgers.map(burger => (
<li key={burger.id}>
<h4>{burger.title}</h4>
<span>{burger.price}</span>
<img src={burger.image} alt={burger.title} />
<p>{burger.description}</p>
<button type="submit" onClick={() => addToCart(burger, "burger")}>Add to cart</button>
</li>
))}
</ul>
</div>
</TabPanel>
<TabPanel>
<div className="lunch">
<h4>Sweet lunch today!</h4>
<span>7$</span>
<p>You can choose one of our 3 sweet crepes + one of our 4 cold drinks!
<br />
Nutella crepe, Crepe with salted caramel and nuts or Oreo Bang crepe with whipped cream and raspberries.
<br />
For drink - one of our homemade lemonades - Melon, Orange or Lemon-Mint. Or a Frozen Coffee!
</p>
<div>
<img src={sweetLunch} alt="Sweet crepe lunch" />
<img src={sweetCrepes} alt="Sweet crepes lunch" />
</div>
</div>
</TabPanel>
<TabPanel>
<div className="crepes">
<ul>
{crepes.map(crepe => (
<li key={crepe.id}>
<h4>{crepe.title}</h4>
<span>{crepe.price}</span>
<img src={crepe.image} alt={crepe.title} />
<p>{crepe.description}</p>
<button type="submit" onClick={() => addToCart(crepe, "crepe")}>Add to cart</button>
</li>
))}
</ul>
</div>
</TabPanel>
</Tabs>
<FontAwesomeIcon className="cart" icon={["fas", "shopping-cart"]} onClick={() => setCart(cart)}/>
</>
)
}
I want to make Cart component appear above Menu component, display on the right side and take a half of screen(like on Upwork when you click on jod in feed) when I click on Cart icon.
So I tried to import Cart component (which also holds a Form) into Menu component
const Cart = ({ cart }) => {
const { image, title, price } = cart;
return (
<>
<li>
<img src={image} alt={title} />
<h4>{title}</h4>
<span>{price}</span>
</li>
<Form />
</>
)
}
and got
TypeError: Cannot destructure property 'image' of 'cart' as it is undefined.
at Cart (Cart.jsx:6)
My App.jsx looks like this
import React, { useState } from "react";
import {
BrowserRouter as Router,
Route,
Switch,
Redirect
} from "react-router-dom";
import './App.css';
import './Responsive.css';
const Header = React.lazy(() => import("./components/Header"));
const Footer = React.lazy(() => import("./components/Footer"));
const Home = React.lazy(() => import("./components/Home"));
const Menu = React.lazy(() => import("./components/Menu"));
function App() {
const [cart, setCart] = useState([]);
return (
<Router>
<React.Suspense fallback={<p className="loader">Loading...</p>}>
<Header />
<Switch>
<Route path="/home" render={props => <Home {...props} />} />
<Route path="/menu" render={props => <Menu cart={cart} {...props} />} />
<Route exact path="/">
<Redirect to="/home" />
</Route>
</Switch>
<Footer/>
</React.Suspense>
</Router>
);
}
export default App;
props which you try to pass is an empty table
const [cart, setCart] = useState([]);
<Menu cart={cart} />
After some restructuring I managed to find an answer for displaying cart items
{cart.map((el) => (
<Cart
key={el.id}
product={el}
removeFromCart={removeFromCart}
/>
))}
And Cart component itself
export default function Cart ({ product, removeFromCart }) {
const { image, title, price } = product;
Total Newbie in React and most of my learning is done through experimentation.
I have the following components:
App.js
import React, { Component } from 'react';
// Libraries and Utilities
import { BrowserRouter, Switch, Route } from 'react-router-dom';
// Components
import Layout from './components/layout/Layout';
import Home from './components/home/Home';
import Login from './components/login/Login';
class App extends Component {
static displayName = App.name;
render() {
return (
<BrowserRouter basename='/myapp>
<Layout>
<Switch>
<Route path="/" exact component={Home} />
<Route path='/login' component={Login} />
<Route path='/admin' component={Admin} />
</Switch>
</Layout>
</BrowserRouter>
);
}
}
export default App;
Layout.js
import React, { Component } from 'react';
import NavMenu from '../navigation/NavMenu';
class Layout extends Component {
render() {
return (
<div className="container-fluid">
<div className="row">
<NavMenu />
</div>
<div className="main layout">
{this.props.children}
</div>
<div className="row">
<Footer />
</div>
</div>
);
}
};
export default Layout;
NavMenu.js
import React, { Component } from 'react';
import { NavLink } from "react-router-dom";
import logo from '../../assets/logo.svg';
class navigation extends Component {
constructor(props) {
super(props)
this.state = {
loggedIn: false
}
}
render() {
return (
<div className="row">
<nav className="navbar navbar-expand navbar-dark bg-primary fixed-top">
<a className="navbar-brand" href="/">
<img src={logo} width="250" height="70" alt="" />
</a>
<div className="collapse navbar-collapse">
<ul className="navbar-nav mr-auto">
<li className="nav-item" to={'/'}>
<NavLink exact={true} className="navbar-brand" activeClassName='active' to='/'>Home</NavLink>
</li>
<li className="nav-item" to={'/admin1'}>
<NavLink exact={true} className="navbar-brand" activeClassName='active' to='/admin1'>Admin 1</NavLink>
</li>
<li className="nav-item" to={'/admin2'}>
<NavLink exact={true} className="navbar-brand" activeClassName='active' to='/admin1'>Admin 2</NavLink>
</li>
</ul>
<ul className="navbar-nav">
<li className="nav-item">
<NavLink exact={true} className="navbar-brand" activeClassName='active' to='/login'>
<i className="fa fa-sign-in" aria-hidden="true"></i>
</NavLink>
</li>
</ul>
</div>
</nav>
</div>
)
};
};
export default navigation;
Login .js
import React, { Component } from 'react';
class login extends Component {
constructor(props) {
super(props)
this.state = {
loginModel: {
UserName: '',
Password: ''
}
}
this.handleInputChange = this.handleInputChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleInputChange(event) {
const target = event.target
const value = target.type === 'checkbox' ? target.checked : target.value
const name = target.name
this.setState(prevState => ({
loginModel: {
...prevState.loginModel,
[name]: value
}
}))
}
handleSubmit(event) {
// At This Stage, I perform an API Call (via axios) and I get the Response Data.
const url = 'some url'
axios.post(url, this.state.loginModel).then((response) => {
if (response.status === 200) {
// Get token from response to be used in authenticated api calls.
const responseData = response.data
let authToken = responseData.token
console.log('authToken', authToken)
swal({
title: "My Application",
text: "Logon Successful.",
icon: "success"
}).then((value) => {
// Go to the Admin Home.
const path = '/admin'
this.props.history.push(path);
})
}
}, (err) => {
console.log(err.response)
const msg = err.response.data.message
const icon = err.response.data.icon
swal({
title: "My Application",
text: msg,
icon: icon
})
})
)
event.preventDefault();
}
componentDidMount() {
}
render() {
return (
<form className="form-signin" onSubmit={this.handleSubmit} >
<h3>Sign In</h3>
<div className="form-group">
<label>Username</label>
<input type="text" className="form-control" autoComplete="off"
id="input-username" name="UserName"
value={this.state.loginModel.UserName}
onChange={this.handleInputChange}
placeholder="Enter Username" />
</div>
<div className="form-group">
<label>Password</label>
<input type="password" className="form-control" autoComplete="off"
id="input-password" name="Password"
value={this.state.loginModel.Password}
onChange={this.handleInputChange}
placeholder="Enter Password" />
</div>
<button type="submit" className="btn btn-primary btn-block">Submit</button>
</form>
)
}
}
export default login;
Home.js
import React from 'react';
import { NavLink } from "react-router-dom";
const home = (props) => {
return (
<div className="container-fluid">
<div className="fill">
<div className="row">
<div className="col-md-4 col-sm-12">
<div className="card">
<div className="card-body flex-fill">
<h5 className="card-title">Info 1</h5>
<p className="card-text">
Details about Info 1
</p>
</div>
<div className="card-footer">
<NavLink exact={true} className="btn btn-primary btn-block" to='/info1'>Start</NavLink>
</div>
</div>
</div>
<div className="col-md-4 col-sm-12">
<div className="card">
<div className="card-body flex-fill">
<h5 className="card-title">Info 2</h5>
<p className="card-text">
Details about Info 2
</p>
</div>
<div className="card-footer">
<NavLink exact={true} className="btn btn-info btn-block" to='/info2'>Browse</NavLink>
</div>
</div>
</div>
<div className="col-md-4 col-sm-12">
<div className="card">
<div className="card-body flex-fill">
<h5 className="card-title">Info 3</h5>
<p className="card-text">
Details about Info 3
</p>
</div>
<div className="card-footer">
<NavLink exact={true} className="btn btn-success btn-block" to='/info3'>View</NavLink>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default home;
My approach is quite simple. The application shows the Home component on initial load.
In the Navmenu, I have a link that navigates me to the Login screen.
In the Login screen, I have a login form where I am able to validate the user name and password via an API call.
I have multiple questions as I am still learning, but adding them here altogether as I feel it is all related.
Questions:
In my NavMenu component, I want to hide the admin1 and admin2 when on
initial load, and show it when the login is successful in the Login
component.
I want to prevent user from going to the route /admin1 and
/admin2 unless they are logged in. I am trying to read Protected
Routes but I am unable to get the hang of it as of yet.
In my Login screen, after successful login, one of the return value of the API
call is an API Key I can use for authorized calls. How can I make
that available such that I can access it from anywhere I perform an
API call.
I hope I provided enough context on what I am trying to achieve here. I know I need to brush up further my skills on how data communication between React happens.
Update: Been reading about Hooks, but I am unsure how to implement it here. Would I need to convert my JS files to use functional approach rather than class structure (ES6)?
Gracias.
I use redux for checking if use if logged in or not.
So when app start before show anything I check this.
and then I have privateRoute.
import React from "react";
import { Redirect, Route } from "react-router-dom";
import { useSelector } from "react-redux";
function PrivateRoute({ component: Component, ...props }) {
const isAuthenticated = useSelector(state => state.User.isLogin);
return (
<Route
render={props =>
isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
{...props}
/>
);
}
export default PrivateRoute;
and define IsAuthenticated component to redirect home page if user is already logged in:
import React from "react";
import { Redirect, Route } from "react-router-dom";
import { useSelector } from "react-redux";
function IsAuthenticated({ component: Component, ...props }) {
const isAuthenticated = useSelector(state => state.User.isLogin)
return (
<Route
{...props}
render={props =>
!isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/",
state: { from: props.location }
}}
/>
)
}
/>
);
}
export default IsAuthenticated;
and define my routes here if user must authenticated to see them:
<Router>
<Switch>
{privateRoutes.map(({ path, component: Component }, idx) => (
<PrivateRoute
key={idx}
exact
path={path}
component={() => (
<SideNav>
<Component />
</SideNav>
)}
/>
))}
<IsAuthenticated exact path="/login" component={LoginPage} />
</Switch>
</Router>
here I use SideNav in my pages, you can use redux state in your Navbar component and with checking that you can show or hide whatever you want.
Or you can define two layout for your two types of your pages.
If you don't want to use redux, you can define state in App.js and pass it to your component or use react context.
I hope I helped you in your question.
I'm trying to display the details of a clickable object on a new page. I've tried a few examples from React Router Pass Param to Component with limited success.
The only one that "kind of" worked was Alexander Luna's suggestion to access via ID in component. However, while this returns the id number, I can't access any other values, like "title".
I have tried globalStore, however, the error message told me that it isn't defined. I'm not sure that's my best option or not.
Ultimately I want the whole object back as I plan to use context with See 'D' below.
App) I have commented out my previous attempts
class App extends Component {
render() {
return (
<React.Fragment>
<Navbar />
<Switch>
<Route exact path="/" component={ProductList} />
<Route path="/cart" component={Cart} />
<Route exact path="/details/:id" component={Details} />
{/* <Route exact path="/details/:id" render={(props) => <Details globalStore={globalStore}
{...props} /> } /> */}
{/* <Route exact path="/details/:id" render={(props)=>{ <Details id={props.match.params.id}/>}}
/> */}
<Route component={Default} />
Details page I want to render in.
import React, { Component } from "react";
export default class Details extends Component {
render() {
return(
<div>
<h2>{this.props.match.params.id}</h2>
<h2>{this.props.match.params.title}</h2>
</div>
The product page, I'm using this link to click through to details.
xport default class Product extends Component {
render() {
const { id, title, img, price, inCart } = this.props.product;
return (
<ProductWrapper className="col-9 mx-auto col-md-6 col-lg-3 my-3">
<div className="card">
<div className="img-container" onClick={() => console.log('you clicked container')}
>
<Link to={`/details/${ this.props.product.id }`} >
<img src={img} alt="product" className="card-img-top" />
</Link>
D - This is how the original code looked, I want to use the {title} tags but I don't know if I need "value => " etc.
<ProductConsumer>
{value => {
const {
id,
company,
img,
info,
price,
title,
size,
} = value.Product;
return (
<div className="container py-5">
{/* title */}
<div className="row">
<div className="col-10 mx-auto text-center text-slanted text-blue my-5">
<h1>{title}</h1>
</div>
</div>
You need an extra parameter
<Route exact path="/details/:id/:title" component={Details} />
export default class Details extends Component {
render() {
return(
<div>
<h2>{this.props.match.params.id}</h2>
<h2>{this.props.match.params.title}</h2>
</div>
);
}
}
// In Product component
<Link to={`/details/${ this.props.product.id }/${this.props.product.title}`} >
<img src={img} alt="product" className="card-img-top" />
</Link>
Try to read the params in the constructor like this:
constructor(props)
{
super(props)
const { match: { params } } = this.props;
var id = params.id
this.state = {
id : id,
}
}
and then read the id from the state.
If you want to pass the whole object you can send it through the url in base64 like this :
<Link to={`/details/+btoa( this.props.product )} >
<img src={img} alt="product" className="card-img-top" />
</Link>
And recieving it in the constructor like the previous snippet en parse it to string with the function atob() and then to json.