Ive made a search and filtering bar as part of an application im making in React. The current way it works is suggestions appear as you type. However there is no handler for if the user just clicks the submit button. At the moment clicking the submit button will take you to a results page with the query in the page URL.
I would like this to be passed as a state when you click the link. This link could then be displayed in the Results component.
Ive attempted this but im fairly new to React so any help would be appreciated.
Heres the search component:
import * as React from 'react';
import { useState } from "react";
import { Link } from "react-router-dom";
const content = [
{link: '/review/elden-ring', name: 'Elden\nRing'},
{link: '/review/', name: 'defg'},
{link: '/review/', name: 'ghij'},
{link: '/review/', name: 'jklm'},
]
export default function Search(props) {
//For storing and setting search input
const [query, setQuery] = useState("");
return (
//Search input
<div class="flex flex-col z-10">
<form class="text-black ml-5 py-0.5 lg:py-0 flex border-2 border-gray-400 rounded-md bg-white px-1">
<input id="searchInput" class="focus:outline-none" type="text" placeholder="Search" value={query} onChange={event => {setQuery(event.target.value)}}/>
<div class="flex mt-1.5"> {/* Flex container to align the icon and bar */}
<Link to={{pathname: "/results/" + query, state: {query}}}> {/* Error handler as search is strick */}
<button type="submit" onClick={() => setQuery(() => "")}>
<svg class="fill-current h-auto w-4 " xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> {/* ! Font Awesome Pro 6.1.0 by #fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. */}
<path d="M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128c0 70.58-57.42 128-128 128S79.1 278.6 79.1 208z" />
</svg>
</button>
</Link>
</div>
</form>
{/* Search Suggestions */}
<div class="ml-5 px-0.5">
{/* Query must have length to prevent mapping by default */}
{query.length > 0 && content.filter((content) => {
//If input return object
if (query == "") {
return content
}
//If any input characters much object characters return corresponding object
else if (content.name.toLowerCase().includes(query.toLocaleLowerCase())) {
return content
}
})
//Maps element based on the number of json objects
.map((content) => {
return(
<div class="bg-white rounded-sm">
<Link to={content.link} onClick={() => setQuery(() => "")}><p>{content.name}</p></Link>
</div>
);
})};
</div>
</div>
);
};
Heres the Results component
import * as React from 'react';
export default function Results(props) {
return (
<h1>{props.location.state.query}</h1>
);
};
Routes
import * as React from 'react';
import './app.css';
import { Routes, Route } from "react-router-dom";
import Header from './components/header/header';
import Footer from './components/footer';
import Error from './components/error';
import Results from './components/results';
import Index from './components/index/index';
import ReviewsPage from './components/reviews/reviewsPage';
import Review from './components/reviews/review';
export default function App() {
return (
<>
<Header />
<Routes>
<Route path="/" element={<Index />} />
<Route path="/reviews" element={<ReviewsPage />} />
{/* Render review with ID for switch statment */}
<Route path="/review/:id" element={<Review />} />
<Route path="/results/:id" element={<Results />} />
<Route path="*" element={<Error />} />
</Routes>
<Footer />
</>
);
};
Search component import line 30
import * as React from 'react';
import Search from './search';
import { useState } from 'react';
import { Link } from 'react-router-dom';
export default function Header() {
//State to toggle navlinks on small screens
const [state, setState] = useState(false)
return (
<nav className=" w-full bg-red-500 shadow-lg relative max-h-[4.1rem]"> {/* Max height set to avoid search suggestions increasing header size */}
<div className="flex justify-between py-3.5 w-full px-3 md:w-2/3 md:px-0 m-auto">
{/* Logo */}
<Link className="text-2xl font-semibold text-white hover:animate-pulse whitespace-nowrap" to="/">GAME REVIEWS</Link>
<div className="flex max-h-[3rem]"> {/* Container to prevent flex effecting both parents container */}
{/* Links */}
{!state && (
<ul id="links" className=" h-40 lg:h-auto flex-col flex lg:flex-row absolute lg:relative mt-10 lg:mt-0 right-0 lg:right-auto px-10 lg:px-0 bg-red-500 rounded-lg lg:rounded-none shadow-sm lg:shadow-none">
<li className="m-5 lg:my-0 lg:mx-5">
<Link className="text-2xl text-white border-none hover:border-solid border-b-2 border-white" to="/">Home</Link>
</li>
<li className="m-5 lg:my-0 lg:mx-5">
<Link className="text-2xl text-white border-none hover:border-solid border-b-2 border-white" to="/reviews">Reviews</Link>
</li>
</ul>
)}
{/* Search bar */}
<Search />
{/* Hamburger */}
<div id="hamburger" onClick={() => setState(!state)} className=" space-y-2 ml-5 mt-2 block cursor-pointer lg:hidden">
<div className="w-6 h-0.5 bg-white"></div>
<div className="w-6 h-0.5 bg-white"></div>
<div className="w-6 h-0.5 bg-white"></div>
</div>
</div>
</div>
</nav>
)
}
Heres an example of what I want to achieve
User searches 'game'
Upon clicking the icon on the right they should be redirected to my results page. This page should show what they just entered on submit.
You can use dynamic route in the Link component which passes query in the URL. And to parse it in Result component, you can use match props.
To navigate change your Link component to
<Link to={"/results/" + query} />
And to parse the query in Result component, use
<h1>{props.match.params.id}</>
If you want the page results to be shared, you must include on the url the search term something like: www.yourdomain.com/review/elden-ring
Take a look and you will see that I've defined that the review route now expects a parameter. You should use that parameter to check all the data you need to display on the page.
And had to edit the Search component because you're using class instead of className for styling.
On the Results component I use the useParams hook to get the url params and show it on the h1. You should use this param as a key to retrieve the actual details of the review of your API.
This is how I'd do (all the logic):
On the App component I define the routes:
<Routes>
<Route exact path="/" element={<Search />} />
<Route path="/review/:query" element={<Results />} />
</Routes>
On the Search component:
// Router v6 hook to navigate the user
const navigate = useNavigate();
const queryRef = useRef(null) // Reference to the input
// Navigates the user to reviews/what they've written
const queryHandler = () => navigate(`/reviews/${queryRef.current.value}`);
return (
<>
// This is where the user types the query
<input type='text' ref={queryRef} placeholder='Search' />
<Icon onClick={queryHandler} /> // This has the onClick to hndle the click
</>
)
And on the results component:
const params = useParams(); // This retrieves all the params defined for the url
<h1>{params.query}</h1>
The query you're sending in the history.push() method must be an object. Instead, you are sending a string. Change it to object like below
props.history.push({
pathname: '/results',
state: { query }
});
For the following route.
localhost:3000/search/?query=ABCD
The following code on load extract the ABCD from query=ABCD and set to the state.
export default function App() {
const [query, setQuery] = useState(() => {
const q = new URLSearchParams(window.location.search);
console.log(q.toString());
return q.get("query") || "";
});
return (
<div className="App">
<h1>Query</h1>
<h2>{query}</h2>
</div>
);
}
So this way you can extract the info from a route.
Now if you want to know how to move from one page to another
assuming you are using some routing library, look at how you can change the route
history.push(`/search/?query={query}`)
is a way to use with react router ( ensure you use the useHistory hook for it )
It was far simpler than I thought and something I had done on a different page
Creating a state for the input.
Setting input as the variable in that state (query).
Setting the value as the input using an onClick on the button.
The link then provided the state variable with the route.
const [query, setQuery] = useState("");
<form
className="text-black ml-5 py-0.5 lg:py-0 flex border-2 border-gray-400 rounded-md bg-white px-1"
>
<input
id="searchInput"
className="focus:outline-none"
type="text"
placeholder="Search"
value={query}
onChange={(event) => {
setQuery(event.target.value);
}}
/>
{/* Flex container to align the icon and bar */}
<div className="flex mt-1.5">
<Link to={{ pathname: "/results/" + query }}>
<button type="submit" onClick={() => setQuery(() => "")}>
<svg
className="fill-current h-auto w-4 "
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
{/* ! Font Awesome Pro 6.1.0 by #fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. */}
<path d="M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128c0 70.58-57.42 128-128 128S79.1 278.6 79.1 208z" />
</svg>
</button>
</Link>
</div>
</form>
Then in the route the path is given a variable (id)
<Route path="/results/:id" element={<Results />} />
This could then be pulled in the results page by useParams.
And assigned to my h1 tag.
import { useParams } from "react-router-dom";
export default function Results(props) {
const {id} = useParams();
return (
<h1>{id}</h1>
);
};
Thank you for everyone help and guidance.
Related
For some reason when I click on one of the posts in the collection of posts section to view the info on one single post, my code is not adding a forward slash / in the URL like so : http://localhost:3000/postyou-just-could-not-have where the URL I need is http://localhost:3000/post/you-just-could-not-have.
Is there a way to add a / to the URL or figure out why it is not being generated?
App.js with Route <Route path="/post/:slug" element={<SinglePost />} /> in question :
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Home from './components/Home';
import About from './components/About';
import SinglePost from './components/SinglePost';
import Post from './components/Post';
import Project from './components/Project';
import NavBar from './components/NavBar';
function App() {
return (
<Router>
<NavBar />
<Routes>
<Route path="/" element={<Home />} exact />
<Route path="/about" element={<About />} />
<Route path="/post/:slug" element={<SinglePost />} />
<Route path="/post" element={<Post />} />
<Route path="/project" element={<Project />} />
</Routes>
</Router>
);
}
export default App;
SinglePost.js component attempting to build URL from slug value :
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import sanityClient from '../client';
import BlockContent from '#sanity/block-content-to-react';
import imageUrlBuilder from '#sanity/image-url';
const builder = imageUrlBuilder(sanityClient);
function urlFor(source) {
return builder.image(source);
}
export default function SinglePage() {
const [singlePost, setSinglePost] = useState(null);
const { slug } = useParams();
useEffect(() => {
sanityClient
.fetch(
`*[slug.current == "${slug}"]{
title,
_id,
slug,
mainImage{
asset->{
_id,
url
}
},
body,
"name": author->name,
"authorImage": author->image
}`
)
.then((data) => setSinglePost(data[0]))
.catch(console.error);
}, [slug]);
if (!singlePost) {
return <div>Loading...</div>;
}
return (
<main className="bg-gray-200 min-h-screen p-12">
<article className="container shadow-lg mx-auto bg-green-100 rounded-lg">
<header className="relative">
<div className="absolute h-full w-full flex items-center justify-center p-8">
<div className="bg-white bg-opacity-75 rounded p-12">
<h1 className="cursive text-3xl lg:text-6xl mb-4">
{singlePost.title}
</h1>
<div className="flex justify-center text-gray-800">
<img
src={urlFor(singlePost.authorImage).url()}
alt="bob"
className="w-10 h-10 rouded-full"
/>
<p className="cursive flex items-center pl-2 text-2xl">
{singlePost.name}
</p>
</div>
</div>
</div>
<img
src={singlePost.mainImage.asset.url}
alt="gary"
className="w-full object-cover rounded-t"
style={{ height: '400px' }}
/>
</header>
<div className="px-16 lg:px-48 py-12 lg:py-20 prose lg:prose-xl max-w-full">
<BlockContent
blocks={singlePost.body}
projectId="notReally"
dataset="production"
/>
</div>
</article>
</main>
);
}
Adding Post.js :
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import sanityClient from '../client';
export default function Post() {
const [postData, setPost] = useState(null);
useEffect(() => {
sanityClient
.fetch(
`*[_type == "post"]{
title,
slug,
mainImage{
asset->{
_id,
url
},
alt
}
}`
)
.then((data) => setPost(data))
.catch(console.error);
}, []);
//anything wrapped in Link makes it clickable
return (
<main className="bg-green-100 min-h-screen p-12">
<section className="container mx-auto">
<h1 className="text-5xl flex justify-center cursive">Blog Post Page</h1>
<h2 className="text-lg text=gray-600 flex justify-center mb-12">
Welcome Doggies
</h2>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{postData &&
postData.map((post, index) => (
<article>
<Link to={'/post' + post.slug.current} key={post.slug.current}>
<span
className="block h-64 relative rounded shadow leading-snug bg-white border-l-8 border-green-400"
key={index}
>
<img
src={post.mainImage.asset.url}
alt="photo"
className="w-full h-full rounded-r object-cover absolute"
/>
<span className="block relative h-full flex justify-end items-end pr-4 pb-4">
<h3 className="text-gray-800 text-lg font-blog px-3 py-4 bg-red-700 text-red-100 bg-opacity-75 rounded">
{post.title}
</h3>
</span>
</span>
</Link>
</article>
))}
</div>
</section>
</main>
);
}
Links are relative to the route hierarchy by default, unless given a value begin with /.
More about Link
In <Post />, try one of the following:
<Link to={post.slug.current} key={post.slug.current}>
...
/Link>
Or:
<Link to={'/post/' + post.slug.current} key={post.slug.current}>
...
/Link>
These will hopefully generate the right path as /post/:slug.
I have a site that is rendering cards of music artists then allowing the user to click on a button "View Ticket Activity" on each artist's card, which then allows more information to show up. Styled using Tailwind (the class names) and using react-router-v6.
I defined my artists state to successfully pull all the seeded artists from a rails DB and I output these artists all at once via a component called <ArtistsDisplay /> (which is routed to '/artists', where I pass down artists={artists} from the parent component <App />. In <ArtistsDisplay />, each artist maps out.
I have a separate component called <EachArtistCard /> that is routed (in <App />) to '/artists/:id' and is redirected there by a useNavigate() within <ArtistDisplay />. The problem there is that artist passes down from the parent <ArtistDisplay /> to the child <EachArtistCard /> and thisArtist=undefined on the first pass, which then breaks the map function within <EachArtistCard />. I'm defining it as thisArtist in place of artist within as a just in case to not have conflict name-wise with the const thisArtist = artists.find( (artist) => parseInt(id) === parseInt(artist.id) );
Within <ArtistsDisplay /> is my major issue. I've tried many re-writes (replacing the new component with an in-component modal, reconfiguring routes, etc) but fundamentally I need to understand why I have the thisArtist=undefined rendering issue. I'm not in strictMode (afaik) as I've seen the double-render-in-development posts so it's gotta be my error somewhere in my writing of this app.
ArtistsDisplay.js
function ArtistsDisplay({ artists, user, searchTerm, setSearchTerm }) {
let navigate = useNavigate();
return (
<div class='bg-base-900 py-6 sm:py-8 lg:py-12'>
<div class='form-control'>
<label class='flex input-group input-group-lg'>
<span>SEARCH</span>
<input
type='text'
onChange={(e) => setSearchTerm(e.target.value)}
placeholder='Search for your favorite artists here...just start typing'
class='input input-bordered w-full input-lg text-center'
/>
</label>
</div>
<div>
<div class='mx-auto max-w-screen-xl px-4 md:px-8'>
<div class='mb-10 md:mb-16'>
<h1 class='mb-4 text-center text-6xl font-thin text-primary md:mb-6 lg:text-7xl'>
ARTISTS
</h1>
<p class='mx-auto uppercase text-center max-w-screen-md text-secondary text-gray-500 md:text-lg'></p>
</div>
<div class='grid gap-8 mx-6 sm:grid-cols-2 sm:gap-12 lg:grid-cols-3 '>
{artists
.filter((artist) => {
if (searchTerm === '') {
return artist;
} else if (
artist.name.toLowerCase().includes(searchTerm.toLowerCase())
) {
return artist;
}
})
.map((artist) => (
<div>
<div
key={artist.id}
class='card w-96 max-w-xs bg-neutral text-neutral-content shadow-xl'>
<div class='card-body p-4 m-2 mx-0 items-center text-center'>
<div class='avatar'>
<div class='w-30 rounded'>
<img
src={artist.image}
alt='a small avatar of the musical artist'
/>
</div>
</div>
<h1 class='card-title'>{artist.name}</h1>
<p>{artist.genre.name}</p>
<div class='card-actions justify-end'>
<button
class='btn btn-primary'
onClick={() => navigate(`/artists/${artist.id}`)}>
view ticket activity
</button>
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</div>
);
}
export default ArtistsDisplay;
EachArtistCard.js
note: even with the two useEffects below commented out, thisArtist still =undefined
import React from 'react';
import { useParams } from 'react-router-dom';
import { useEffect, useState } from 'react';
import IndividualPost from './IndividualPost';
function EachArtistCard({ posts, setPosts, artists }) {
let { id } = useParams();
const thisArtist = artists.find(
(artist) => parseInt(id) === parseInt(artist.id)
);
//* to set selling & looking
useEffect(() => {
thisArtist.posts.map((each) => {
if (each.for_sale === true) {
setSelling(selling + 1);
} else {
setLooking(looking + 1);
}
});
}, []);
//* to set upcomingShows
useEffect(() => {
thisArtist.concerts.map((each) => setUpcomingShows(upcomingShows + 1));
}, []);
return (
<div>
<div class='bg-base-900 py-6 sm:py-8 lg:py-'>
<div class='mx-auto max-w-screen-xl px-4 md:px-8'>
<div class='mb-10 md:mb-16'>
<h1 class='mb-4 text-center text-6xl font-thin uppercase text-primary md:mb-6 lg:text-7xl'>
{thisArtist.name}
</h1>
</div>
<div class='flex justify-center'>
<div class='card w-96 bg-base-500 bg-neutral text-neutral-content justify-center shadow-2xl'>
<div class='avatar'>
<div class='w-30 rounded'>
<img
src={thisArtist.image}
alt='a small avatar of the music thisArtist'
/>
</div>
</div>
<div class='card-body items-center text-center'>
<h2 class='card-title'>{thisArtist.name}</h2>
<p>
There's {upcomingShows} upcoming concerts listed for{' '}
{thisArtist.name}!
</p>
<div>
<div class='badge badge-primary uppercase'>
{selling} selling
</div>
<div class='badge badge-primary uppercase'>
{looking} looking
</div>
</div>
<div class='card-actions justify-end'>
<button class='btn btn-secondary w-full'>
I have tickets to sell
</button>
<button class='btn btn-secondary w-full'>
I'm Looking For Tickets
</button>
<button class='btn btn-outline btn-black w-full'>
Go Back
</button>
</div>
</div>
</div>
</div>
<h2 class='my-10 text-center text-5xl font-thin uppercase text-primary md:mb-6 lg:text-6xl'>
ALL POSTS
</h2>
</div>
</div>
</div>
);
}
export default EachArtistCard;
App.js
removed a lot of other code not pertaining to the situation here
import '../../src/App.css';
import ArtistsDisplay from './ArtistsDisplay';
import ConcertsDisplay from './ConcertsDisplay';
import VenuesDisplay from './VenuesDisplay';
import GenreDisplay from './GenreDisplay';
import Login from './Login';
import SignUp from './SignUp';
import NotFound from './NotFound';
import Header from './Header';
import { Route, Routes } from 'react-router-dom';
import { useState, useEffect } from 'react';
import UsersPage from './UsersPage';
import EachArtistCard from './EachArtistCard';
function App() {
const [user, setUser] = useState('');
const [sessionInfo, setSessionInfo] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
const [artists, setArtists] = useState([]);
useEffect(() => {
fetch('/artists')
.then((r) => r.json())
.then((info) => setArtists(info));
}, []);
return (
<div>
<Header
user={user}
setUser={setUser}
onLogin={onLogin}
onLogout={onLogout}
loggedIn={loggedIn}
/>
<Routes>
<Route
path='/'
element={
<UsersPage
user={user}
cookies={cookies}
sessionInfo={sessionInfo}
loggedIn={loggedIn}
/>
}
/>
<Route
path='/artists'
element={
<ArtistsDisplay
artists={artists}
genres={genres}
user={user}
posts={posts}
setPosts={setPosts}
searchTerm={searchTerm}
setSearchTerm={setSearchTerm}
showModal={showModal}
setShowModal={setShowModal}
/>
}
/>
<Route
path='/artists/:id'
element={
<EachArtistCard
artists={artists}
concerts={concerts}
posts={posts}
setPosts={setPosts}
user={user}
/>
}
/>
<Route path='*' element={<NotFound />} />
</Routes>
</div>
);
}
export default App;
Will take any steps in the right direction as I'm lost. If more info is needed, please let me know.
This question already has answers here:
Warning: Function components cannot be given refs
(7 answers)
Closed 8 months ago.
I have a HeaderIcon component that looks like this:
function HeaderIcon({ inactiveIcon, activeIcon }) {
const [isActive, setIsActive] = useState(false);
return (
<div onClick={() => setIsActive(!isActive)}>
{isActive ? activeIcon : inactiveIcon}
</div>
);
}
export default HeaderIcon;
When I run my code I got these errors:
Unhandled Runtime Error
Error: Hydration failed because the initial UI does not match what was rendered on the server.
Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
And I checked my console and saw this:
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
This is where I used my HeaderIcon:
function Header() {
return (
<IconContext.Provider value={{ size: "30", color: "#374957" }}>
<header className="flex items-center justify-around py-1.5 px-3 bg-white">
<div className="flex items-center space-x-2 w-full max-w-xs">
<h1 className="text-4xl">Zipp</h1>
</div>
<div className="py-2.5 px-4 flex items-center">
<InputGroup>
<InputLeftElement
pointerEvents="none"
children={<SearchIcon color="grey" />}
/>
<Input
type="text"
bg="whitesmoke"
w={"full"}
focusBorderColor="none"
border={"none"}
placeholder="Search"
/>
</InputGroup>
</div>
{/* right */}
<div className="flex items-center space-x-6">
<div className="headerIcons active:opacity-80">
<Link href="/">
<HeaderIcon
inactiveIcon={<AiOutlineHome />}
activeIcon={<AiFillHome />}
/>
</Link>
</div>
<div className="headerIcons">
<HeaderIcon
inactiveIcon={<MdOutlineAddBox />}
activeIcon={<MdAddBox />}
/>
</div>
<div className="headerIcons -rotate-12">
<HeaderIcon
inactiveIcon={<AiOutlineNotification />}
activeIcon={<AiFillNotification />}
/>
</div>
<div className="cursor-pointer">
<Avatar w={7} h={7} />
</div>
</div>
</header>
</IconContext.Provider>
);
}
Since you are adding a Functional Component inside Next.js's Link tag, there are some changes to be made. Here is an overview of what the say in the documentation:
If the child of Link is a functional component, in addition to using passHref, you must wrap the component in React.forwardRef.
Which means, first you should add passHref prop to Link when using HeaderIcon, this way:
<Link href="/" passHref>
<HeaderIcon
inactiveIcon={<AiOutlineHome />}
activeIcon={<AiFillHome />}
/>
</Link>
Then change HeaderIcon to the following code. Notice I'm using useRouter from Next.js to handle active and inactive state.
import { useRouter } from "next/router";
const HeaderIcon = ({ onClick, href, inactiveIcon, activeIcon }, ref) => {
const router = useRouter();
return (
<a href={href} onClick={onClick} ref={ref}>
<div>{router.pathname ? activeIcon : inactiveIcon}</div>
</a>
);
};
export default React.forwardRef(HeaderIcon);
function HeaderIcon(props) {
const [isActive, setIsActive] = useState(false);
const { inactiveIcon, activeIcon } = props;
return (
<div onClick={() => setIsActive(!isActive)}>
{isActive ? activeIcon : inactiveIcon}
</div>
);
}
export default HeaderIcon;
I hope this will be helpful for you. Thanks
PROBLEM
So I have two files in my auth folder one is called Signin and the other is called SignUp These two files are identical except for the names. all of the tags are the same the only difference is the naming of the file and the component.
For some reason though I am only ever able to see the SignIn component when the other component is created in the exact same way.
Here is the SignUp component, the one that is not being rendered.
import Axios from "axios";
import React, { useState, useContext } from "react";
import '../comStyles.css'
import UserContext from '../../context/UserContext';
import { useHistory } from 'react-router-dom';
export default function SignUp() {
const [email, setEmail] = useState();
const [password, setPassword] = useState();
const { setUserData } = useContext(UserContext);
const history = useHistory();
const submit = async (e) => {
e.preventDefault();
const existingUser = {email, password};
const loginRes = await Axios.post("http://localhost:8174/user/login", existingUser);
setUserData({
token: loginRes.data.token,
user: loginRes.data.token
});
localStorage.setItem("auth-token", loginRes.data.token);
history.push("/");
}
return (
// <div className="w-50 bg-black signinbox align-items-center justify-content-center h-50">
<div className="signinbox w-50 h-50">
<div className="shadow-lg bg-black p-3">
<h1 className="title my-2">UNT LMS</h1>
<form onSubmit={submit}>
<input id="login-email" className="form-control mr-sm-7 my-3" type="email" placeholder="Username" aria-label="Search" onChange={(e) => setEmail(e.target.value)}/>
<input className="form-control mr-sm-7 shadow-lg bg-black rounded" type="password" placeholder="Password" aria-label="Search" onChange={(e) => setPassword(e.target.value)}/>
<button className="bg-black signinbutton btn btn-outline-success my-4" type="submit">Sign Up</button>
</form>
</div>
</div>
);
}
It is being rendered by another component called AuthLinks like this. (Same way for SignIn)
<Link to="/SignUp" className="nav-link">
Sign Up
</Link>
This creates a link that can take you to /SignUp component or page.
However as it does in fact change the url to /SignUp there is nothing to show on the page. The component is failing to render even though if I go to /SignIn it renders to the browser fine.
Here are how the routes are setup in App.js
import { NavBar } from './components';
import SignIn from './components/auth/SignIn';
import SignUp from './components/auth/SignUp';
<Switch>
<Route path="/Books/list" exact component={BooksList} />
<Route path="/Books/create" exact component={BooksInsert} />
<Route path="/Books/update/:id" exact component={BooksUpdate} />
<div className="vh-100 align-items-center justify-content-center">
<Route path="/SignIn" exact component={SignIn} />
</div>
<div className="vh-100 align-items-center justify-content-center">
<Route path="/SignUp" exact component={SignUp} />
</div>
</Switch>
Trouble Shooting
I have read other questions about the component and their capitalization. I don't think that this is the case for this question. As I have tried creating a whole new file called Register and changing everything to /Register.
I tried using Lorem ipsum in case the text or anything was just hiding behind the navbar.
I tried making it the full width and height of the screen the VH as well.
tried to refresh the page.
restarted the server on port 3000
Hello again Stackoverflow members.I have GLS Component I have more similiar components to GLS concept is the same but styling is not, anyway the thing I want to achieve is this, when I press on the Link in GLS/A Component I want to display/Render the image class and price of that Component on the payment page for some reason it is giving me this error 'TypeError: Cannot read property 'image' of undefined'. I would be very grateful if someone could help me.
Gls Component
import React from "react";
import data from "../data.json";
import { Link } from "react-router-dom";
function GLS({product}) {
return (
<div>
<div key={product.id} className="m-4 bg-blue-100 rounded-xl p-8 absolute ">
<div>
<Link
to={`/payment/${product.id}`}
className="py-1 px-2 text-black-600 h-10 ml-24 mt-32 bg-white w-
36 rounded-full focus:outline-none focus:ring-2 focus:ring-gray-600"
>
Buy Now
</Link>
<img
alt=""
className="w-screen object-contain"
src={product.image}
></img>
<h1 className=" ml-24 md:text-5xl sm:text-5xl top-8">
{product.class}
</h1>
<h1 className="text-base font-mono ml-24 top-24">
{product.price}
</h1>
</div>
</div>
</div>
);
}
export default GLS;
App Component
import React,{useState, useEffect} from 'react'
import './assets/main.css'
import A from './Models/GLS Model/A'
import GLS from './Models/GLS Model/GLS'
import data from "./Models/data.json";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
return (
<div className='' >
<div >
<Router>
<Switch>
<Route path="gls">
{data.map((product) => (
<GLS key={product.id} product={product} />
))}
</Route>
<Route path="a">
{data.map((product) => (
<A key={product.id} product={product} />
))}
</Route>
<Route path="/payment/:productId">
<Payment />
</Route>
<Route exact path="/">
<Home />
</Route>
</Switch>
</Router>
</div>
</div>
);
}
export default App;
import React from 'react'
import {
Link,
} from "react-router-dom";
import data from "./Models/data.json";
function Home() {
return (
<div className='ml-20'>
<nav className='bg-red-50 max-w-full'>
<ul >
<li>
<Link to='/gls'>GLS-class</Link>
</li>
<li>
<Link to="/a"> A-Class</Link>
</li>
</ul>
</nav>
</div>
)
}
export default Home
Payment Component
import React from "react";
import { useParams } from "react-router-dom";
import data from "./Models/data.json";
const Payment = () => {
const { productId } = useParams();
const filteredData = data.filter((product) => product.id === productId)[0];
return (
<div className="ml-20">
<img alt="" className="w:2/4 object-contain " src={filteredData.image} />
<h2
className=" ml-24 mlg:ml-6 mlg:mt-2 mlg:static text-5xl mlg:text-2xl text-blue-400 absolute top-
48"
>
{filteredData.class}
</h2>
<h3
className="text-lg mlg:mb-2 mlg:ml-6 mlg:mt-2 mlg:static font-bold text-green-800
font-mono ml-24 top-64 absolute"
>
{filteredData.price}
</h3>
</div>
);
};
export default Payment;
Json file
[
{
"id": 0,
"class": "A-Class",
"image": "./ModImages/Aclass.jpg",
"price": "It goes around $37,300",
},
{
"id": 1,
"class": "GLS-Class",
"image": "./ModImages/GLSclass.jpg",
"price": "It goes around $47,700"
}
]
TypeError: Cannot read property 'image' of undefinedThis is a great error to have because it more or less tells you exactly what's wrong. In basic terms, undefined means that a variable has been declared but has not yet been assigned a value.
function myFunc(product) {
return product.img;
}
var product; //initialized to undefined, must explicitly set type;
console.log(myFunc(product));
Result -> Uncaught TypeError: product is undefined
function myFunc(product) {
return product.img;
};
var product; //undefined
product = {}; //explicitly setting to object
product.img = 'image'; // set a name and value
console.log(myFunc(product));