i'm trying to make multiple pages in react app , and I'm confused how to set my codes properly;I have a component folder which has Navbar.jsx which I have my nav items that i want to send me to another page when i click(react-route) . and I have the Container folder which has Header.jsx and that what i want to change based on the nav items clicked.
import React, { useState } from 'react';
import { RiMenu3Line, RiCloseLine } from 'react-icons/ri';
import './Navbar.css';
import logo from '../../assets/logo.png'
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useRouteMatch,
useParams
} from "react-router-dom";
const Menu = () => (
<>
<Router>
<p><Link to={"/about"}>About</Link></p>
<p><Link to={"/contact"}>Contact</Link></p>
</Router>
</>
)
const Navbar = () => {
...
Header.jsx I didnt change any thing
const Header = () => {
return (...
Related
First time using React this week and I'm trying to create a fetch Github app, everything is working but the scroll to the top. When I scroll down and click on the view Repo I want the screen to automatically scroll up, I've copied the code via the React Docs and I am using React-Router-Dom version 5 however nothing is scrolling up. Please see below for my code, where am I going wrong guys?
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
App.js
import { BrowserRouter as Router, Switch, Route} from 'react-router-dom';
import './App.css';
import RepoPage from './components/RepoPage';
import ScrollToTop from './components/ScrollToTop';
function App() {
return (
<Router>
<div className="App">
<h1>Want to see other people's projects? Enter a Github username and Voila!</h1>
<ScrollToTop />
<Switch>
<Route>
<RepoPage />
</Route>
</Switch>
</div>
</Router>
);
}
export default App;
Snippet of my Repo.js component
import React, {useEffect, useState} from 'react';
import axios from 'axios';
import RepoDetails from './RepoDetails';
import ScrollToTop from './ScrollToTop';
import '../App.css'
function renderRows(repo) {
return(
<div
className='repo-button'
onClick={() => getDetails(repo.name)}
key={repo.id}
>
<h3 className='repo-name'>
{repo.name}
<button onCLick={<ScrollToTop />}>View Repo</button>
</h3>
</div>
)
}
There might be some misunderstanding with how ScrollToTop function component is used. I think when used in the App component, it is meant to automatically scroll to the top when the pathname changes. We can probably add this similar behavior for when the View Repo button is clicked by passing a callback function to onClick like () => window.scrollTo(0, 0) instead of the function component.
Not to worry I've fixed the issue, I've set the window scroll to the top within the button onClick tag. Thanks anyway
In my react app, I am using the useHistory hook to redirect to my Home Component. Why is useHistory undefined, and how would I solve this problem?
App.js
import 'bootstrap/dist/css/bootstrap.css'
import React from "react";
import { Button } from 'react-bootstrap';
import { BrowserRouter, Route, useHistory} from 'react-router-dom';
import { Switch } from 'react-router-dom/cjs/react-router-dom.min';
import './App.css';
import Home from './components/Home/Home';
const App = () =>
{
const history = useHistory()
const handleClick = () =>
{
console.log(history)
console.log("handle click")
// history.push("/home") this line throws "cannot read property of undefined" error
}
return (
<>
<BrowserRouter>
<Button onClick = {handleClick} variant = "primary">Get Started</Button>
<Switch>
<Route path="/home" exact component = {Home}/>
</Switch>
</BrowserRouter>
</>
);
}
export default App;
Try to use the useHistory() outside this component and see it work. It seems you are calling the function before the Router itself and both are imported from react-router-dom .
I expect that console.log('Refresh') runs every time the route changes (switching from Component1 to Component2). But it's only triggering on first render. Why?
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(<BrowserRouter><App /></BrowserRouter>, document.getElementById('root'));
App.js:
import React, { useEffect } from 'react';
import { Switch, Route } from 'react-router-dom';
import Nav from './Nav';
import Component1 from './Component1';
import Component2 from './Component2';
const App = () => {
useEffect( () => console.log('Refresh'));
return (
[<Switch>
<Route component = {Nav}/>
</Switch>,
<Switch>
<Route exact path = '/component1' component = {Component1}/>
<Route exact path = '/component2' component = {Component2}/>
</Switch>]
);
}
export default App;
Nav.js:
import React from 'react';
import { Link } from 'react-router-dom';
const Nav = () => {
return (
<div>
<Link to = '/component1'>Component 1</Link>
<Link to = '/component2'>Component 2</Link>
</div>
);
}
export default Nav;
Component1.js:
import React from 'react';
const Component1 = () => {
return (
<div>
<p>Hi</p>
</div>
);
}
export default Component1;
Component2.js:
import React from 'react';
const Component2 = () => {
return (
<div>
<p>Bye</p>
</div>
);
}
export default Component2;
The useEffect is not triggered because the App component is not re-rendered, nothing changed in that component (no state or props update).
If you want the App component to re-render when the route change, you can use the withRouter HOC to inject route props, like this :
import { Switch, Route, withRouter } from 'react-router-dom';
const App = () => {
useEffect( () => console.log('Refresh'));
return (...);
}
export default withRouter(App);
Example : https://codesandbox.io/s/youthful-pare-n8p1y
use the key attribute so everytime we render new component (different key)
<Route path='/mypath/:username' exact render= {routeProps =><MyCompo {...routeProps} key={document.location.href} />} />
Use the 2nd argument to useEffect to conditionally apply effect. For example via react-router-dom, you get some properties
const { schoolId, classId } = props
useEffect(() => {
// fetch something here
}, [schoolId, classId)
Here [schoolId, classId acts as the unique identifier for useEffect to trigger.
Using Hooks:
use useLocation and useLayoutEffect get more efficiency:
import { useLocation } from "react-router-dom";
//...
const location = useLocation();
//...
useLayoutEffect(() => {
console.log("location",location)
}, [location])
I am looking to write a React hook with React 16.8.6 that will let me scroll to a particular HTML element section on click of a navigation item. I have a Navigation component that is a sibling of the sections rendered on the page.
Also when the page scrolls, I would like to update state of the App with that HTML section.
Navigation Component JSX
<ul class="nav>
<li><a>Section 1</a></li>
<li><a>Section 2</a></li>
</ul>
Sections in Home Page at App Level Component
<section className="section-1">Section 1</section>
<section className="section-2">Section 2</section>
Hooks
const [navItem, setNavItem] = React.useState(null);
const sectionRef = React.useRef(null);
// Scroll To Item
useEffect(() => {
console.log(sectionRef.current);
if (sectionRef.current) {
sectionRef.current.scrollToItem();
}
}, []);
If you don't mind using react-router-dom, then you can track history changes and update the scroll position to an HTML element's id via a hash history change. The advantage of this approach is you don't have to utilize state, nor utilize refs, and it can scale across the entire application (regardless of where the elements are located within the application's tree, you can scroll to them).
Working example:
https://fglet.codesandbox.io/ (demo)
https://codesandbox.io/s/fglet (source -- unfortunately, doesn't work within the codesandbox editor)
components/ScrollHandler (hook that listens to hash history changes, searches for elements that match the id located within the hash and, if it finds a matching element id, then it'll scroll to the element)
import { useEffect } from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router-dom";
const ScrollHandler = ({ location }) => {
useEffect(() => {
const element = document.getElementById(location.hash));
setTimeout(() => {
window.scrollTo({
behavior: element ? "smooth" : "auto",
top: element ? element.offsetTop : 0
});
}, 100);
}, [location]);
return null;
};
ScrollHandler.propTypes = {
location: PropTypes.shape({
pathname: PropTypes.string,
search: PropTypes.string,
hash: PropTypes.string,
state: PropTypes.any,
key: PropTypes.string
}).isRequired
};
export default withRouter(ScrollHandler);
components/Navigation (links to change url hash history location)
import React from "react";
import { Link } from "react-router-dom";
import List from "../List";
const Navigation = () => (
<List>
{[1, 2, 3, 4, 5].map(num => (
<li key={num}>
<Link to={`/#section${num}`}>Section {num}</Link>
</li>
))}
</List>
);
export default Navigation;
components/Sections (the Headline component contains the id that will be matched against)
import React from "react";
import Headline from "../Headline";
const Sections = () =>
[1, 2, 3, 4, 5].map(num => (
<Headline key={num} id={`#section${num}`}>
Section {num}
</Headline>
));
export default Sections;
index.js
import React from "react";
import { render } from "react-dom";
import { BrowserRouter } from "react-router-dom";
import Container from "./components/Container";
import Navigation from "./components/Navigation";
import Sections from "./components/Sections";
import ScrollHandler from "./components/ScrollHandler";
import "./styles.css";
const App = () => (
<BrowserRouter>
<Container>
<ScrollHandler />
<Navigation />
<Sections />
</Container>
</BrowserRouter>
);
render(<App />, document.getElementById("root"));
I am using React Router V6. Some things didn't work and were different. For instance, withRouter was deprecated. React router offered a solution if you need it (link).
My solution for V6:
Create a component WithRouter.jsx:
import { useLocation, useNavigate, useParams } from "react-router-dom";
function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return <Component {...props} router={{ location, navigate, params }} />;
}
return ComponentWithRouterProp;
}
export default withRouter;
Create a component ScrollHandler.jsx
import { useEffect } from "react";
import WithRouter from "./WithRouter";
const ScrollHandler = ({ location }) => {
useEffect(() => {
const element = document.getElementById(location.hash.substring(1));
if (element) element.scrollIntoView();
}, [location]);
return null;
};
export default WithRouter(ScrollHandler);
In index.js I wrapped my <App/> comp with the BrowserRouter as Router like so:
<Router>
<App />
</Router>
Then in App.js, add the <ScrollHandler/> component:
<ScrollHandler location={location} />
I'm using react-router v4.2.2 in my project, and am trying to create a set of cards that each link to other components. Right now I'm just testing that the router works, by routing each Card to one specific component called 'Project1'. This, however, is not working; I'm not seeing the div inside the Project1 component pop up. What am I doing wrong?? Shouldn't each Card link to the Project1 component?
Here is the code for the main container that holds the cards:
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import ProjectCard from '../components/project_card.js';
import Project1 from '../components/project1.js';
class ProjectCards extends React.Component {
render() {
var projectCards = this.props.projects.map((project, i) => {
return (
<div key={i}>
<Link to={`/${project.title}`}>
<ProjectCard title={project.title} date={project.date} focus={project.focus}/>
</Link>
</div>
);
});
return (
<div>{projectCards}</div>
);
}
}
function mapStateToProps(state) {
return {
projects: state.projects
};
}
export default connect(mapStateToProps)(ProjectCards);
Here is the code for the Routes container:
import React from 'react';
import Project1 from '../components/project1.js';
import { connect } from 'react-redux';
import { Route, Switch } from 'react-router-dom';
class Routes extends React.Component{
render() {
var createRoutes = this.props.projects.map((project, i) => {
return <Route key={i} exact path={`/${project.title}`} component={Project1}/>
});
return (
<Switch>
{createRoutes}
</Switch>
);
}
}
function mapStateToProps(state) {
return {
projects: state.projects
};
}
export default connect(mapStateToProps)(Routes);
Here is the code for the index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { applyMiddleware, createStore } from 'redux';
import ReduxPromise from 'redux-promise';
import { BrowserRouter } from 'react-router-dom';
import App from './components/App.jsx';
import css from '../style/style.css';
import style from '../style/style.css';
import reducers from './reducers';
const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root'));
and the code for Project1, which should display when a Card has been clicked:
import React from 'react';
const Project1 = () => {
return (
<div>hello there this is Project1</div>
);
}
export default Project1;
When you click on a link, you navigate to Project1, which has no Routes defined. You basically destroy your Route when you lick on it because the Switch is in the same component as the Link. The Switch statement needs to be moved to a 3rd component so that it still exists after clicking on a linking card.