So I have a page with six Link tags which all need to redirect to one page with different props depending on which link is clicked. Here's how I'm passing props:
<Link href={{
pathname: '/products',
query: {category: 'Starters'}
}}>
<div class="container">
<div class="content">
<div class="content-overlay"></div>
<img class="content-image" src="/starters.png" />
<div class="content-details fadeIn-bottom">
<h3 class="content-title text-uppercase">Starters</h3>
</div>
</div>
<h3>Starters</h3>
</div>
</Link>
On the products page, here's how I'm reading these props:
export default function products(){
const router = useRouter()
const {id} = router.query
console.log(id)
return(
<>
<Navbar></Navbar>
<h1 className="text-center">{id}</h1>
<Footer></Footer>
</>
)
}
The problem here is that my id value is read as undefined or empty. I need to take this value, show it on my page and then send it to an API endpoint which will then use this string to execute a conditional SQL query.
Any idea as to why I'm getting an empty or undefined value and how to fix this?
Please try functional component instead like below. Hooks are used in functional components.
import React, { useState } from "react";
const Products = (props) => {
const [myId, setMyId] = useState<any>(null);
const [myCategory, setMyCategory] = useState<any>(null);
useEffect(() => {
console.log(props.router.query)
setMyId(props.router.query.id)
setMyCategory(props.router.query.category)
}, [props.router]);
return(<h1 className="text-center">{myId}</h1>);
}
Alternatively
import React, { useState } from "react";
import router from 'next/router';
const Products = () => {
const {id, category} = router.query
const [myId, setMyId] = useState<any>(null);
const [myCategory, setMyCategory] = useState<any>(null);
useEffect(() => {
if(!id) {
return;
}
console.log(id);
setMyId(id);
setMyCategory(category);
}, [id]);
return(<h1 className="text-center">{myId}</h1>);
}
Can you also modify your jsx a bit? For example
<Link href={{
pathname: '/products',
query: {id: 1, category: 'Starters'}
}}>
Starters
</Link>
<div class="container">
<div class="content">
<div class="content-overlay"></div>
<img class="content-image" src="/starters.png" />
<div class="content-details fadeIn-bottom">
<h3 class="content-title text-uppercase">Starters</h3>
</div>
</div>
</div>
Related
I want to make it so when you click on a div it redirects you to another page, like react router but I have no knowledge to make it yet. Here is my code:
const Card: React.FC = ({ info }: any) => {
return (
<div className='card stacked featured'>
<img src={info.links.mission_patch} className='card_image' alt='NO-IMAGE'/>
<div className='card_content'>
<h2 className="card_title">{info.mission_name}</h2>
<p className='card_number'>Flight: {info.flight_number}</p>
<p className='card_description'>{info.details}</p>
</div>
</div>
)
}
Basically this is card, the data is from a web api. I want to make it so when I click on a card a whole new page shows with only that card data not other cards because they are iterated.
I suggest using useNavigate from react-router-dom. It is what I use for such things.
import { useNavigate } from 'react-router-dom'
const Card: React.FC = ({info}: any) => {
const navigate = useNavigate()
return (
<div className='card stacked featured'>
<img src={info.links.mission_patch} className='card_image' alt='NO-IMAGE'/>
<div className='card_content' onClick={() => navigate("/toThePageYouWantToNavigateTo")>
<h2 className="card_title">{info.mission_name}</h2>
<p className='card_number'>Flight: {info.flight_number}</p>
<p className='card_description'>{info.details}</p>
</div>
</div>
)
}
Import and render the Link component from react-router-dom.
import { Link } from 'react-router-dom';
...
const Card: React.FC = ({ info }: any) => {
return (
<div className='card stacked featured'>
<img src={info.links.mission_patch} className='card_image' alt='NO-IMAGE'/>
<Link
className='card_content'
to={`"/mission/${info.id}`} // <-- this is path you want to link to
>
<h2 className="card_title">{info.mission_name}</h2>
<p className='card_number'>Flight: {info.flight_number}</p>
<p className='card_description'>{info.details}</p>
</Link>
</div>
);
};
If you don't want to render an actual link/anchor tag into the DOM then import and use the useNavigate hook and add an onClick handler to the div element.
import { Link } from 'react-router-dom';
...
const Card: React.FC = ({ info }: any) => {
const navigate = useNavigate();
return (
<div className='card stacked featured'>
<img src={info.links.mission_patch} className='card_image' alt='NO-IMAGE'/>
<div
className='card_content'
onClick={() => navigate(`"/mission/${info.id}`)} // <-- this is path you want to link to
>
<h2 className="card_title">{info.mission_name}</h2>
<p className='card_number'>Flight: {info.flight_number}</p>
<p className='card_description'>{info.details}</p>
</div>
</div>
);
};
So I have these 2 components:
First One MAIN PAGE
`
import {useEffect, useState} from 'react';
import Navbar from "./navbar";
import Modal from "./Modal";
import '../styles/home.css'
import FavoriteCrypto from "./favoriteCrypto";
export default function MainPage() {
const[data, setData] = useState([])
const[input, setInput] = useState("");
const [openModal, setOpenModal] = useState(false)
const [modalArr, setModalArr] = useState([])
const[favorites, setFavorites] = useState([])
const url = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false"
useEffect(()=>{
fetch(url)
.then((res)=>{
return res.json()
})
.then((data)=>{
setData(data)
})
},[])
let searchBar = data.filter((e)=>{
return e.id.toLowerCase().includes(input.toLowerCase())
})
// add to favorite
function addToFav(id){
if(!favorites.includes(id)){
setFavorites(favorites.concat(id))
}
}
function openModalFunc(id) {
setOpenModal(true);
if(!modalArr.includes(id)) {
setModalArr(modalArr.concat(id))
}
}
function closeModalFunc(id) {
setOpenModal(false);
setModalArr([]);
}
let modalRender = data.filter(data => modalArr.includes(data.id));
let favoriteRender = data.filter(data => favorites.includes(data.id))
console.log(favoriteRender)
return(
<div>
<Navbar input={input} setInput={setInput}/>
<div className='general-info'>
<h4>Coin</h4>
<h4 className='p'>Price</h4>
<h4 className='c'>Change</h4>
<h4 className='mc'>Market Cap</h4>
<h4 className='s'>Supply</h4>
</div>
<Modal addFavorite = {addToFav} modalArr={modalRender} close = {closeModalFunc} open = {openModal}/>
{searchBar.map((e)=>(
<div
onClick={()=>{
openModalFunc(e.id);
}}
className='all_coins_wrapper'>
<div className='coins-wrapper'>
<div className='coins-label'>
<img src={e.image} alt=""/>
<div className='general_info'>
<div>{e.name}</div>
<div>{e.symbol.toUpperCase()}</div>
</div>
</div>
<p className='price-main'>${e.current_price}</p>
</div>
<div className='left-part'>
<p className='change'>{e.price_change_percentage_24h}</p>
<div className='marcap'>{e.market_cap}</div>
<div className='circ'>{e.circulating_supply}</div>
</div>
</div>
)
)}
</div>
)
}
SECOND ONE :
`
import React from "react";
import Navbar from "./navbar";
import MainPage from "./home";
export default function FavoriteCrypto({favorite}){
return(
<div>
</div>
)
}
I want to import these variable '
let favoriteRender = data.filter(data => favorites.includes(data.id))
from the first component to the second one in order to display on the second page the favoirite coins'
I tried to copy paste the code from the first component to the second component and to import the variable, but that didnt work. I am using react for a week now.So sorry if this question is already ask.But I cant solve this issue.
You don't need to export that variable in order to pass data between components. You can use props in-order to do so.
Here is the link to the docs.
And here is an example of doing so:
// COMPONENT
const MyNameComponent = (props) => <h1>{props.name}</h1>;
// USAGE
const App = () => {
const name = "John Doe";
return <MyNameComponent name={name} />
}
As a solution to your problem could be:
<FavoriteCrypto favourite={favouriteRender} />
and using it inside the component to display it. You can align the data according to your wish. Read the docs for more info 👍.
I have a NavBar component which holds login information on the user. When the user is logged in it says "Welcome" along with the user details. I want to implement the same idea in another component so that when a user posts a blog, it says "Posted By: " along with the users log in details. How would I pass the details form NavBar.js to Products.js ?
import React, { useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
const NavBar = (props) => {
const providers = ['twitter', 'github', 'aad'];
const redirect = window.location.pathname;
const [userInfo, setUserInfo] = useState();
useEffect(() => {
(async () => {
setUserInfo(await getUserInfo());
})();
}, []);
async function getUserInfo() {
try {
const response = await fetch('/.auth/me');
const payload = await response.json();
const { clientPrincipal } = payload;
return clientPrincipal;
} catch (error) {
console.error('No profile could be found');
return undefined;
}
}
return (
<div className="column is-2">
<nav className="menu">
<p className="menu-label">Menu</p>
<ul className="menu-list">
<NavLink to="/products" activeClassName="active-link">
Recipes
</NavLink>
<NavLink to="/about" activeClassName="active-link">
Help
</NavLink>
</ul>
{props.children}
</nav>
<nav className="menu auth">
<p className="menu-label">LOGIN</p>
<div className="menu-list auth">
{!userInfo &&
providers.map((provider) => (
<a key={provider} href={`/.auth/login/${provider}?post_login_redirect_uri=${redirect}`}>
{provider}
</a>
))}
{userInfo && <a href={`/.auth/logout?post_logout_redirect_uri=${redirect}`}>Logout</a>}
</div>
</nav>
{userInfo && (
<div>
<div className="user">
<p>Welcome</p>
<p>{userInfo && userInfo.userDetails}</p>
<p>{userInfo && userInfo.identityProvider}</p>
</div>
</div>
)}
</div>
);
};
export default NavBar;
This is a snippet from Products.js, where I want the user details data to be passed to:
<footer className="card-footer ">
<ButtonFooter
className="cancel-button"
iconClasses="fas fa-undo"
onClick={handleCancelProduct}
label="Cancel"
/>
<ButtonFooter
className="save-button"
iconClasses="fas fa-save"
onClick={handleSave}
label="Save"
/> Posted By: {}
</footer>
One way is to use state variable in parent component of both footer and navbar, then passing into navbar as prop function to set the state variable to the userInfo, and in footer you can now use the userInfo
//beginning of parent component
const [userInfo, setUserInfo] = useState(null);
...
//navbar component
<NavBar setUserInfoParent={setUserInfo}/>
...
//footer component
<footer>
Posted By: {userInfo && userInfo.userDetails}
</footer>
There will likely be many opinions on this as there are many ways to accomplish storing some Global state.
Assuming your project will be a decent size and you don't want to keep all of this data in a component and pass it down through/to each component, I would look at these few options:
Context API: https://reactjs.org/docs/context.html
RTK: https://redux-toolkit.js.org/tutorials/quick-start (my preference)
And many others these days including Flux, Zustand, Mobx, Recoil...and on and on..
I would like to know why my "topbar" is duplicated when I only want one. And it's since I did my import of the user image via axios and added the .map in the return. I really don't understand why if someone could help me that would be nice. Thanks in advance
import "./topbar.css"
import { Search } from '#mui/icons-material'
import { useState, useEffect, Fragment } from 'react'
import axios from "axios"
function Home() {
const [user, setPosts] = useState([])
useEffect(() => {
console.log("chargement ok")
const fetchData = async () => {
const result = await axios.get(
'http://localhost:4200/api/user/')
setPosts(result.data)
}
fetchData();
}, [])
return (
<Fragment>
{ user
? user.map((users,topbar) => ( <div key={topbar} className="topBarContainer">
<div className="topBarLeft">
<span className="logo">Groupomania</span>
</div>
<div className="topBarCenter">
<div className="searchBar">
<Search className="searchIcon" />
<input placeholder="Vous cherchez quelque chose ?" className="searchInput" />
</div>
</div>
<div className="topBarRight">
<div className="topBarLinks">
<span className="topBarLink">Page d'acceuil</span>
<span className="topBarLink">Deconnexion</span>
</div>
<img src={users.picture} alt="Photo de profil de l'utilisateur" className="topBarImg" />
</div>
</div>))
: (<p></p>)
}
</Fragment>
)
}
export default Home
I'm not sure why, but it may be because of your key.
Some patterns to fix first:
const [user, setPosts] = useState([]) -> const [posts, setPosts] = useState([])
you don't have to use the word Fragment: -> <>
Normally in a .map params are used like this posts.map((post, index) => ...)
posts ? post.map(...) : null
Edit: of course you have to remove your topbar from your .map(...)
Now try with a better key than "topbard" that is the index in the array ... try post.id that should be uniq
Edit solution:
import "./topbar.css";
import { Search } from "#mui/icons-material";
import { useState, useEffect, Fragment } from "react";
import axios from "axios";
function Home() {
const [user, setUser] = useState();
useEffect(() => {
console.log("chargement ok");
const fetchData = async () => {
const result = await axios.get("http://localhost:4200/api/user/");
setUser(result.data);
};
fetchData();
}, []);
return (
<div className="topBarContainer">
<div className="topBarLeft">
<span className="logo">Groupomania</span>
</div>
<div className="topBarCenter">
<div className="searchBar">
<Search className="searchIcon" />
<input
placeholder="Vous cherchez quelque chose ?"
className="searchInput"
/>
</div>
</div>
<div className="topBarRight">
<div className="topBarLinks">
<span className="topBarLink">Page d'acceuil</span>
<span className="topBarLink">Deconnexion</span>
</div>
{user && <img
src={user.picture}
alt="Photo de profil de l'utilisateur"
className="topBarImg"
/>}
</div>
</div>
);
}
export default Home;
As the map is rendering the topbar for every user, you get as many topbars as there are users.
The map function should be inside the top bar container div.
<div key={key} className="topBarContainer">
{ user.map(...) }
</div>
This is because your are making the topbar inside the loop,So you are getting a topbar per user.
I have been struggling with this for some time and I am not sure how to solve the issue.
Basically, I am trying to render some components onto my Index page, this is my code below:
App.js
import Index from "./Components/Index"
import axios from "axios"
export default function App() {
const [movieList, setMovieList] = React.useState([])
let featured = []
let coming = []
let showing = []
React.useEffect(() => {
console.log("Ran App Effects")
axios.get(`API_CALL_TO_GET_LIST_OF_MOVIES`)
.then(res =>{
setMovieList(res.data)
})
}, [])
return(
<div>
{movieList.map(movie =>{
if(movie.status === 'featured'){
featured.push(movie.api_ID)
} else if (movie.status === 'upcoming'){
coming.push(movie.api_ID)
} else{
showing.push(movie.api_ID)
}
})}
<Index featured={featured} coming={coming} showing={showing}/>
</div>
)
}
In the code above I am receiving an array of Objects and based on what is in their status I am putting them in some empty arrays and sending them as props into my Index component.
This is what my index component looks like:
import React from "react"
import Header from "./Header"
import Footer from "./Footer"
import MovieCard from "./MovieCard"
import axios from "axios"
export default function Index(props) {
const [featuredMovies, setFeaturedMovies] = React.useState([])
const [comingMovies, setComingMovies] = React.useState([])
//const featured = [419704,338762,495764,38700,454626,475557]
//const coming = [400160,514847,556678,508439,524047,572751]
React.useEffect(() => {
console.log("Ran Effect")
axios.all(props.featured.map(l => axios.get(`API_CALL_TO_GET_SPECIFIC_MOVIE/${l}`)))
.then(axios.spread(function (...res){
setFeaturedMovies(res)
}))
.catch((err) => console.log(err))
axios.all(props.coming.map(l => axios.get(`API_CALL_TO_GET_SPECIFIC_MOVIE/${l}`)))
.then(axios.spread(function (...res){
setComingMovies(res)
}))
.catch((err) => console.log(err))
}, [])
return(
<body>
<Header />
<section className="home">
<div className="container">
<div className="row">
<div className="col-12">
<a className="home__title">FEATURED MOVIES</a>
</div>
{ featuredMovies.map(movie =>{
return <MovieCard movie={movie} featured={true} />
}) }
{console.log(props.featured)}
</div>
</div>
</section>
<section className="home">
<div className="container">
<div className="row">
<div className="col-12">
<a className="home__title">COMING SOON</a>
</div>
{ comingMovies.map(movie =>{
return <MovieCard movie={movie} featured={false} />
})}
</div>
</div>
</section>
<Footer/>
</body>
)
}
The issue I am running into is, whenever I run the app for the first time it works fine but then when I hit the refresh button the components do not render anymore
The only time it re-renders when I refresh the page is when I uncomment,
//const featured = [419704,338762,495764,38700,454626,475557]
//const coming = [400160,514847,556678,508439,524047,572751]
and replace the props.featured.map and props.coming.map with featured.map and coming.map hence using the hard coded values and not the values passed in from the props.
Any help with this would be much appreciated as I am completely stuck and currently pulling my hair out.
I took the liberty to tinker with your code. In the example below I've rearranged the data into three sets with the help of useMemo and by checking the status property of each movie. It is important to keep any data related logic outside of the render logic.
I also moved around some of your HTML structure. You were outputting a <body> tag inside of a <div>. The outer layer should be in control of the outer HTML structure, so I moved that HTML to the App component.
import { useState, useEffect, useMemo } from 'react'
import Header from "./Components/Header"
import Footer from "./Components/Footer"
import Index from "./Components/Index"
import axios from "axios"
export default function App() {
const [movieList, setMovieList] = useState([])
const featuredMovies = useMemo(() => {
return movieList.filter(({ status }) => status === 'featured');
}, [movieList]);
const upcomingMovies = useMemo(() => {
return movieList.filter(({ status }) => status === 'upcoming');
}, [movieList]);
const showingMovies = useMemo(() => {
return movieList.filter(({ status }) => status !== 'featured' && status !== 'upcoming');
}, [movieList]);
useEffect(() => {
axios.get(`API_CALL_TO_GET_LIST_OF_MOVIES`)
.then(res =>{
setMovieList(res.data)
})
}, [])
return (
<body>
<Header />
<Index data={featuredMovies} title="Featured Movies" featured={true} />
<Index data={upcomingMovies} title="Coming Soon" />
<Index data={showingMovies} title="Showing Now" />
<Footer/>
</body>
)
}
Since we now have three sets of movies (featured, upcoming, and playing) it would also make sense to have three components that handle those data sets instead of having one that handles multiple. Each Index component gets its own data set and other props to render the movies within it.
import MovieCard from "./MovieCard"
export default function Index({ data, title, featured = false }) {
return (
<section className="home">
<div className="container">
<div className="row">
<div className="col-12">
<a className="home__title">{title}</a>
</div>
{data.map(movie => {
return <MovieCard movie={movie} featured={featured} />
})}
</div>
</div>
</section>
);
}