I have a page where each component has scroll functionality. But it will come from another page.
Like on the Home page route is "/" I have set the scroll with
import { Link as Scrolllink, animateScroll as scroll } from 'react-scroll'
<Scrolllink
onClick={() => history.push('/services')}
to="DayCare"
spy={true}
smooth={true}
hashSpy={true}
isDynamic={true}
>
Day care
</Scrolllink>
<Scrolllink
onClick={() => history.push('/services')}
to="Office"
spy={true}
smooth={true}
hashSpy={true}
isDynamic={true}
>
Office
</Scrolllink>
Similar to this I have set for all the target elements.
Now In-service page I ave added the id of that target before that components
<div id="DayCare"> <DayCare /></div>
<div id="HomeApartment"> <HomeApartment/></div>
<div id="Office"> <Office/></div>
<div id="MoveInOut"> <MoveInOut/></div>
<div id="Construction"> <Construction/></div>
<div id="Airbnb"> <Airbnb/></div>
<div id="Carpet"> <Carpet/></div>
<div id="Infection"> <Infection/></div>
But I am pushing to the "/service" page, but not scrolled to target id:
If I click on On the link I going to a random component, not the one I have targeted.
How to fix this problem.
I had the same problem with my app and resolve it with async function and scroller from react-scroll:
import React from 'react';
import {scroller} from "react-scroll";
import {useHistory} from "react-router-dom";
const Nav = () => {
const history = useHistory();
const scrollTarget = (target) => scroller.scrollTo(target, {smooth: true, duration: 700});
const scrollToPage = async (target) => {
if (history.location.pathname !=='/') {
await history.push('/');
}
scrollTarget(target);
};
return (
<nav>
<div onClick={() => scrollToPage('about')}>About</div>
<div onClick={() => scrollToPage('info')}>Info</div>
<div onClick={() => scrollToPage('contact')}>Contact</div>
</nav>
);
}
export default Nav;
Maybe you can try the react-scrollable-anchor package https://www.npmjs.com/package/react-scrollable-anchor. For me it worked for navigating and scrolling from another page.
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'm pretty much new in React/Web development and just can't figure it out regarding ReactPlayer.
I have a .JSON file with [ID, Question, URL] and I load the questions into divs. What I want is when I click the div(question) then the URL that is assigned to that question should load in the ReactPlayer..
This is how it looks so far:
import React, { useState } from "react";
import Questions from "../data/questions.json";
import style from "./Card.module.css";
import ReactPlayer from "react-player/youtube";
function Card() {
const handleClick = (item) => {
console.log(item);
};
return (
<div>
<div className={style.ViewContent}>
<div className={style.mainCard}>
{ListQuestions.map((ListItem, index) => {
return (
<div onClick={() => handleClick(ListItem.url)} key={index} className={style.Card}>
<h3 className={style.Titel}>{ListItem.question}</h3>
</div>
);
})}
</div>
<div className={style.VideoPlayer}>
<ReactPlayer url={handleClick.item} controls={true} />
</div>
</div>
</div>
);
}
export default Card;
I tested the click function and every time I click the question the console logs only the URL.
But how can the ReactPlayer get that URL and play the video?
I'm sorry for the bad coding.. still learning :)
I tried adding onSubmit on the div box so when clicking the div it should submit/load the link to the ReactPlayer... but thinking logically and then interpreting it kind of does not work.
I figured it out :D
import React, { useState } from "react";
import Questions from "../data/questions.json";
import style from "./Card.module.css";
import ReactPlayer from "react-player";
function Card() {
const [playUrl, setPlayUrl] = useState(""); ← here you could put the youtube link to show up when loading the page.
const [isPlaying, setIsPlaying] = useState(true);
return (
<div>
<div className={style.ViewContent}>
<div className={style.mainCard}>
{ListQuestions.map((ListItem, index) => {
return (
<div onClick={() => setPlayUrl(ListItem.url)} key={index} className={style.Card}>
<h3 className={style.Titel}>{ListItem.question}</h3>
</div>
);
})}
</div>
<div className={style.VideoPlayer}>
<ReactPlayer url={playUrl} controls={true} playing={isPlaying} />
</div>
</div>
</div>
);
}
export default Card;
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'm trying to add and remove a class when clicking on an item of my header, but I struggle to do it and I don't know how to map the rendered items in the header component.
Here's the first part of the code with a function that works for routing and window.location.
I'm able to add the class but it gets added to each element clicked and it gets removed only when I click again on it.
import React, { useState } from 'react';
const Link = ({ href, children }) => {
const [activeItem, setActiveItem] = useState(false);
const onClick = (event) => {
if (event.metaKey || event.ctrl) {
return;
}
event.preventDefault();
window.history.pushState({}, '', href);
const navEvent = new PopStateEvent('popstate');
window.dispatchEvent(navEvent);
setActiveItem(!activeItem);
};
return (
<a
onClick={onClick}
className={`item ${activeItem ? 'active' : ''}`}
href={href}
>
{children}
</a>
);
};
export default Link;
Here's my header element instead:
import React from 'react';
import Link from './Link';
import Logo from './Logo';
const Header = () => {
return (
<div className="ui secondary pointing menu">
<Link href="/">
<Logo />
</Link>
<div className="pointing right menu">
<Link href="/services">services</Link>
<Link href="/works">works</Link>
<Link href="/contacts">contacts</Link>
</div>
</div>
);
};
export default Header;
You need to make your link components aware of each other by lifting the state to your header component. Then you pass you tell your link components which link is currently selected by passing it as a prop and you also need to give them the ability to change which link is currently selected:
import React from 'react';
import Link from './Link';
import Logo from './Logo';
const Link = ({ href, children, isActive, handleClick }) => {
const onClick = (event) => {
if (event.metaKey || event.ctrl) {
return;
}
event.preventDefault();
window.history.pushState({}, '', href);
const navEvent = new PopStateEvent('popstate');
window.dispatchEvent(navEvent);
handleClick();
};
return (
<a
onClick={onClick}
className={`item ${isActive ? 'active' : ''}`}
href={href}
>
{children}
</a>
);
};
export default Link;
const Header = () => {
const [activeLink, setActiveLink] = useState(0)
return (
<div className="ui secondary pointing menu">
<Link
href="/"
isActive={activeLink === 0}
handleClick={() => setActiveLink(0)}
>
<Logo />
</Link>
<div className="pointing right menu">
<Link
href="/services"
isActive={activeLink === 1}
handleClick={() => setActiveLink(1)}
>
services
</Link>
<Link
href="/works"
isActive={activeLink === 2}
handleClick={() => setActiveLink(2)}
>
works
</Link>
<Link
href="/contacts"
isActive={activeLink === 3}
handleClick={() => setActiveLink(3)}
>
contacts
</Link>
</div>
</div>
);
};
export default Header;
I'm using image search and display app. Users can click on a photo and a modal would pop up. Those modal would have id in the url. However when I refresh the page, the modal isn't there and an error is shown. I get the url from unsplash api so with page refresh reload the url is gone. How do I Keep the url in url query so that the url persists even on page refresh?
Lisitem
import React, { useState } from "react";
import { Link, BrowserRouter as Router, Route } from "react-router-dom";
import ModalWrapper from "./ModalWrapper";
const ListItem = ({ photo }) => {
return (
<>
<Router>
<div key={photo.id} className="grid__item card">
<div className="card__body">
<Link to={{ pathname: `/${photo.id}`, state: photo }}>
<img src={photo.urls.small} alt="" />
</Link>
<Route path="/:photoId" component={ModalWrapper} />
</div>
</div>
</Router>
</>
);
};
export default ListItem;
Modal wrapper
import React from "react";
import Modal from "react-modal";
import { useHistory, useLocation } from "react-router-dom";
const customStyles = {
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)"
}
};
Modal.setAppElement("#root");
function ModalWrapper() {
const history = useHistory();
const location = useLocation();
const photo = location.state;
function downloadImage() {}
function close() {
history.push("/");
}
return (
<Modal isOpen={true} onRequestClose={close} style={customStyles}>
<img src={photo.urls.small} alt="" />
<div>
<button onClick={close} className="button">
Close
</button>
<button onClick={downloadImage()}>Download</button>
</div>
</Modal>
);
}
export default ModalWrapper;
The reason why it doesn't work when you refresh the page is because the photo that you passed as a param while navigating is no longer available. But, pathname is something that's still available (because it's part of the URL itself)
So, on the ModalWrapper page, you can check if photo is absent, then make a new API call to get the photo based on the id that is available in the pathname. I've never used unsplash API but I think it would be this API.
Your ModalWrapper would look like this
function ModalWrapper() {
const history = useHistory();
const location = useLocation();
const [photo, setPhoto] = useState(location.state);
useEffect(() => {
if (location.pathname && !location.state) {
// call the new API here using pathname (photo ID) and setPhoto
console.log(location.pathname);
}
}, [location]);
function downloadImage() {}
function close() {
history.push("/");
}
return (
!!photo && (
<Modal isOpen={true} onRequestClose={close} style={customStyles}>
<img src={photo.urls.small} alt="" />
<div>
<button onClick={close} className="button">
Close
</button>
<button onClick={downloadImage()}>Download</button>
</div>
</Modal>
)
);
}
You haven't asked this but, I would also move the Router and Route outside the ListItem and keep it in App.js (wrapping everything in there with Router). Keeping it in ListItem is like having a router and route for each list-item, which is not something you would ideally want. You would want to keep one router and route across the application, and it usually belongs to App.js or a wrapper or sorts. Here's the codesandbox after such changes