"scrollIntoView cannot read properties of 'null'" error in React - javascript

I am receiving the error Uncaught TypeError: Cannot read properties of null (reading 'scrollIntoView') when trying to click on an item on my Navbar.
It's bit of a tricky issue, but if I load the page from scratch, it will throw the above error. However, if I save the project in VSCode again, it will work perfectly fine until I refresh the page. Is there anyway to correct this issue?
Navbar.jsx:
import { useState } from 'react';
import { Link } from 'react-router-dom';
import '../App.css';
import myAvatar from '../images/avataaars.png';
function Navbar() {
const aboutPage = document.getElementById('about');
const home = document.getElementById('home');
const skills = document.getElementById('skills');
const [navbar, setNavbar] = useState(true);
const handleNavbarToggle = (e) =>{
setNavbar(!navbar);
}
return(
<>
<div className='navbar-container'>
<Link to='/'><img src={myAvatar} className='nav-logo'/></Link>
<ul>
<li onClick={() =>{home.scrollIntoView({behavior: 'smooth'})}}>Home</li>
<li onClick={() =>{aboutPage.scrollIntoView({behavior: 'smooth'})}}>About</li>
<li onClick={() => {skills.scrollIntoView({behavior: 'smooth'})}}>Skills</li>
<li>Projects</li>
<li id='navbar-login-btn'>Login</li>
</ul>
<div className={`navbar-toggle ${navbar ? '' : 'open'}`} onClick={handleNavbarToggle}>
<span></span>
<span></span>
<span></span>
</div>
</div>
<div className={`navbar-header ${navbar ? 'navbar-header-hide' : ''}`}>
<ul>
<Link className='navbar-header-item' to='/'>Home</Link>
<Link className='navbar-header-item' to='/about'>About</Link>
<Link className='navbar-header-item' to='/skills'>Skills</Link>
<Link className='navbar-header-item' to='/projects'>Projects</Link>
<Link className='navbar-header-item' to='/login'>Login</Link>
</ul>
</div>
</>
)
}
export default Navbar;
App.js:
import { useState, useEffect } from 'react';
import { Route, Routes } from 'react-router-dom';
import TextTransition, { presets } from 'react-text-transition';
import './App.css';
import Navbar from './components/Navbar';
import Homepage from './components/Homepage';
import About from './components/About';
import Skills from './components/Skills';
import myAvatar from './images/avataaars.png';
function App() {
return (
<>
<Navbar />
<div className='homepage-container-web'>
<Homepage />
<About />
<Skills />
</div>
<div className='homepage-container-devices'>
<Routes>
<Route path='/' element={<Homepage />} />
<Route path='/about' element={<About />}/>
</Routes>
</div>
</>
);
}
export default App;
About.jsx:
import '../App.css';
function About() {
return(
<>
<div className='about-container' id='about'>
<h2 class='about-title'>A little bit about me...</h2>
<p>Hi, my name is Andrew! I'm 20 years old, and I have <a href='https://www.crohnscolitisfoundation.org/what-is-crohns-disease' target="_blank" style={{color: '#a61bee'}}>Crohn's Disease</a>. Ever since I was 12 years old I have had an interest in technology, software engineering, cybersecurity, firefighting, and cars. I currently work for the Department of Defense and hold a Senior IT Specialist position. I am always looking to learn and improve myself.</p>
</div>
<div className='about-ending-container' />
</>
)
}
export default About;

The issue is that the first time it renders, when you do this
const aboutPage = document.getElementById('about');
const home = document.getElementById('home');
const skills = document.getElementById('skills');
those elements don't exist yet. The simplest solution would be to just change it to
const aboutPage = () => document.getElementById('about');
const home = () => document.getElementById('home');
const skills = () => document.getElementById('skills');
// ...
<li onClick={() =>{home().scrollIntoView({behavior: 'smooth'})}}>Home</li>
<li onClick={() =>{aboutPage().scrollIntoView({behavior: 'smooth'})}}>About</li>
<li onClick={() => {skills().scrollIntoView({behavior: 'smooth'})}}>Skills</li>
so that it does the search at the time of the click. That's not a very "react" way of doing it. But it should "just work".
If you want to do it the "right way", I supposed you'd want to use useRef, and hoist that up to the App. Using just home as an example, and omiting non-relevant code, it would be something like:
function App() {
const homeRef = useRef();
// ...
<Navbar homeRef={homeRef} />
// ...
<Homepage ref={homeRef} />
}
function Navbar({ homeRef }) {
const scrollIntoView = (ref) => {
if (ref.current) {
ref.current.scrollIntoView({behavior: 'smooth'})
}
}
<li onClick={() =>{scrollIntoView(homeRef)}}>Home</li>
}
const Homepage = React.forwardRef((props, ref) => {
return(
<>
<div className='home-container' id='hone' ref={ref} >
//...
</div>
</>
)
})

Related

HTML not showing up on React webpage [duplicate]

This question already has an answer here:
react-router-dom v6 Routes showing blank page
(1 answer)
Closed 18 hours ago.
So as the title suggests, Im looking for a reason as to why my webpage is not showing up on the website
Here is the App.js file
import { useState, useEffect } from "react";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import axios from "axios";
import Post from "./Post.js";
function App() {
const [posts, setPosts] = useState([]);
useEffect(() => {
axios.get("https://jsonplaceholder.typicode.com/posts").then((response) => {
setPosts(response.data);
});
}, []);
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">All Posts</Link>
</li>
</ul>
</nav>
<Routes>
<Route path="/" exact>
<h1>All Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link to={`/posts/${post.id}`}>{post.title}</Link>
</li>
))}
</ul>
</Route>
<Route path="/posts/:id">
<Post />
</Route>
</Routes>
</div>
</Router>
);
}
export default App;
I made Post.js a separate js file from App like this:
import { useState, useEffect } from "react";
import axios from "axios";
function Post() {
const [post, setPost] = useState(null);
const id = window.location.pathname.split("/")[2];
useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`).then((response) => {
setPost(response.data);
});
}, [id]);
if (!post) {
return <div>Loading...</div>;
}
return (
<div>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
);
}
export default Post;
The site is running as well. I can access everything that is there, but for some reason, it is just a blank page.
I hope you can help me with this issue. Thank you
You cannot directly put the content to be rendered under <Route>. Instead, set the element attribute on the <Route>.
For example:
<Route path="/posts/:id" element={<Post/>}/>
You also need to extract the content for all posts into a separate component.
<Route path="/" element={<AllPosts/>}>

How to add React routing to mapped list items?

I'm studying React and trying to make a Todo app. I made a first section and a menu which should switch the app between two sections. I'm mapping buttons in order to add them class because they should have different styling after they are clicked. Now I need to add routing to these buttons but I can't make it work.
This is my code:
import React, { useState } from "react";
import Todo from "../Todo/Todo";
import About from "../About/About";
import { BrowserRouter as Router, Route, Link, Routes } from "react-router-dom";
import classnames from "classnames";
import styles from "./App.module.css";
const App = () => {
const [clicked, setClicked] = useState("");
const buttons = ["About me", 'Todos'];
return (
<Router>
<div>
<nav className={styles.menu}>
<ul className={styles.nav}>
{buttons.map((item, index) => {
return (
<li
key={index}
onClick={() => {
if (clicked === index) {
setClicked();
} else {
setClicked(index);
}
}}
>
<Link to={`/buttons/${index}`}>
{
<button
className={classnames({ [styles.button]: true, [styles.selected]: clicked === index
})}
>
{item}
</button>
}
</Link>
</li>
);
})}
</ul>
</nav>
<Routes>
<Route path="/" exact element={<About />} />
<Route path="/todo" element={<Todo />} />
</Routes>
</div>
</Router>
);`
export default App;
The error message I get in console is 'No routes matched location "/buttons/0"'. And the same for "/buttons/1".
Probably I indicated paths wrong somehow but I don't know how to fix it.

How to make API data a link once fetched to take user to another page to display data

I am trying to make a API call and return the JSON object in an unordered list in Component A. Once I make the unordered list, I need to make the name property a link which will take me to component B and show the data related to the name property. How can I achieve this?
Component A (Home.js)
import React , { useState} from "react";
import axios from 'axios';
import Display from './Display'
function Home(){
const [info, setInfo] = useState([]);
const [orgName, setOrg] = useState([]);
const [dataToMove, setDataToMove] = useState([]);
let signal = axios.CancelToken.source();
function handleChange(event) {
setOrg(event.target.value);
}
function handleClick(event) {
axios.get(`https:/api.github.com/orgs/${orgName}/repos`, {
cancelToken: signal.token,
})
.then(res => {
const info = res.data;
setInfo(info);
setDataToMove(info);
console.log(info);
}).catch(err => {
console.log(err);
});
}
// < Display parentToChild={this.state.dataToMove}>
//|Image: <img src={dummy.owner.avatar_url} alt="" />
const parentToChild = () => {
let data ={};
data = dataToMove;
}
let getInfo = info;
return (
<div>
<span>Enter User Id</span>
<input type="text" onChange={handleChange}/>
<button onClick={handleClick}>Get Posts By User</button>
<button onClick={parentToChild} > Go to Display Page</button>
<ul>
{
getInfo.map(dummy =>
(
<li key={dummy.id} >
Name: {dummy.name}
|| Fork: {dummy.fork ? "true" : "false"} || Owner: {dummy.owner.login}
|| Avatar: {} || Open Issues: {dummy.open_issues_count}}
</li>
))
}
</ul>
</div>
);
}
export default Home;
Component B (Display.js)
function Display({parentToChild}){
const info =[];
return (
<div>
<span>See Data Below</span>
<ul>
{
info.map(dummy =>
(
<li key={dummy.id} >
Name: {dummy.name}
|| Fork: {dummy.fork ? "true" : "false"} || Owner: {dummy.owner.login}
|| Avatar: {} || Open Issues: {dummy.open_issues_count}} ||Image: <img src={dummy.owner.avatar_url} alt=""/>
</li>
))
}
</ul>
<p> {parentToChild}</p>
</div>
);
}
export default Display;
Navigation.js
import React from "react";
import { NavLink } from "react-router-dom";
function Navigation() {
return (
<div className="navigation">
<nav className="navbar navbar-expand navbar-dark bg-dark">
<div className="container">
<NavLink className="navbar-brand" to="/">
React Multi-Page Website
</NavLink>
<div>
<ul className="navbar-nav ml-auto">
<li className="nav-item">
<NavLink className="nav-link" to="/">
Home
<span className="sr-only">(current)</span>
</NavLink>
</li>
<li className="nav-item">
<NavLink className="nav-link" to="/about">
Display Data
</NavLink>
</li>
</ul>
</div>
</div>
</nav>
</div>
);
}
export default Navigation;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import {Navigation, Display, Home }from './Components';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Router>
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/Display" element={<Display />} />
</Routes>
</Router>
</React.StrictMode>
);

How to use :params in React router

Having issues with the react-router picking up on a nested route. A param is passed to accommodate for any subsequent links to be conditionally rendered later in the child component passed into Route
import React from "react";
import { Route } from "react-router-dom";
import CollectionsOverview from "../../components/collections-overview/collections-overview";
import CollectionPage from "../collection/collection";
const ShopPage = ({ match }) => {
return (
<div className="shop-page">
<Route exact path={`${match.path}`} component={CollectionsOverview} />
<Route path={`${match.path}/:collectionId`} component={CollectionPage} />
</div>
);
};
export default ShopPage;
import React from "react";
import "./collection.styles.scss";
const CollectionPage = ({ match }) => {
return (
<div className="collection-page">
<h2>COLLECTION PAGE: {match.params.collectionId}</h2>
</div>
);
};
export default CollectionPage;
Route 2 never renders its component on route manipulation
You can use useParams. Please check this example:
import React from "react";
import {BrowserRouter as Router,Switch,Route,Link,useParams} from "react-router-dom";
export default function ParamsExample() {
return (
<Router>
<div>
<h2>Accounts</h2>
<ul>
<li>
<Link to="/netflix">Netflix</Link>
</li>
<li>
<Link to="/zillow-group">Zillow Group</Link>
</li>
<li>
<Link to="/yahoo">Yahoo</Link>
</li>
<li>
<Link to="/modus-create">Modus Create</Link>
</li>
</ul>
<Switch>
<Route path="/:id" children={<Child />} />
</Switch>
</div>
</Router>
);
}
function Child() {
let { id } = useParams();
return (
<div>
<h3>ID: {id}</h3>
</div>
);
}
Source

What is the best practice to close a DropDown on a Link click in React?

I have the following code: Link to Sandbox
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route, Switch, Link } from "react-router-dom";
import "./styles.css";
function DropDown({ close }) {
return (
<ul>
<li>
<Link to="/" onClick={close}>
Page 1
</Link>
</li>
<li>
<Link to="/page2" onClick={close}>
Page 2
</Link>
</li>
<li>
<p>Dont hide dropdown when clicking me!</p>
</li>
</ul>
);
}
function Header() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(prev => !prev)}>Toggle DropDown</button>
{isOpen && <DropDown close={() => setIsOpen(false)} />}
</div>
);
}
function Page1() {
return <h1>Page 1</h1>;
}
function Page2() {
return <h1>Page 2</h1>;
}
function App() {
return (
<div className="App">
<BrowserRouter>
<Header />
<Switch>
<Route exact path="/" component={Page1} />
<Route path="/page2" component={Page2} />
</Switch>
</BrowserRouter>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
How can I implement this without always passing an close callback down to every link in my dropdown?
I am trying to implement this with a hook which listens on mousedown and checks if it is inside the dropdown and a click on type A but the problem is that then it closes the dropdown before the react redirect to the route.
The click outside I already have covered with a useClickAway hook but I would kind of need a useClickInsideOnLink hook as well.
I think you have (at least) two options:
move Header into the pages (due to new pages are rendered, the header is re-initialized). Forked your CodeSandbox here
let the DropDown handle it's state on it's own, and include the toggle button to the dropdown. See CodeSandbox here
Another possible way (I don't recommend it as it adds unnecessary complexity to a simple problem) (added by the comments of the asker):
Actually, a component updates, when props are changed or the state
changes. To quote react docs: If you want to “reset” some state when
a prop changes, consider either making a component fully controlled
or fully uncontrolled with a key instead. Fully uncontrolled would be
option 2 then.
To trigger the change of props, you could connect the Header withRouter.
This way the Header component get's notified when the location (and
other routing-props) change. Based on that, you could apply updates to the isOpen
state via the lifecycleMethod
componentDidUpdate
and compare the previous location.pathname with the current one and set the isOpen back to false if the pathname changed.
Actually I do not recommend that. But here you go:
CodeSandbox
You could create an internal component inside your DropDown component and use the close function by default, also passing the other props via rest parameter. Something like this:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route, Switch, Link } from "react-router-dom";
import "./styles.css";
function DropDown({ close }) {
const MyLink = ({...rest}) => {
return (
<Link {...rest} onClick={close}>
Page 1
</Link>
)
}
return (
<ul>
<li>
<MyLink to="/">
Page 1
</MyLink>
</li>
<li>
<MyLink to="/page2">
Page 2
</MyLink>
</li>
<li>
<p>Dont hide dropdown when clicking me!</p>
</li>
</ul>
);
}
function Header() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(prev => !prev)}>Toggle DropDown</button>
{isOpen && <DropDown close={() => setIsOpen(false)} />}
</div>
);
}
function Page1() {
return <h1>Page 1</h1>;
}
function Page2() {
return <h1>Page 2</h1>;
}
function App() {
return (
<div className="App">
<BrowserRouter>
<Header />
<Switch>
<Route exact path="/" component={Page1} />
<Route path="/page2" component={Page2} />
</Switch>
</BrowserRouter>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Probably it's too late but if it can't help someone :
You can destrcuture pathname from useLocation() hook, and on every pathname change
you can set isOpen to false in useEffect.
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route, Switch, Link, useLocation } from "react-router-dom";
import "./styles.css";
function DropDown({ close }) {
const MyLink = ({...rest}) => {
return (
<Link {...rest}>
Page 1
</Link>
)
}
return (
<ul>
<li>
<MyLink to="/">
Page 1
</MyLink>
</li>
<li>
<MyLink to="/page2">
Page 2
</MyLink>
</li>
<li>
<p>Dont hide dropdown when clicking me!</p>
</li>
</ul>
);
}
function Header() {
const [isOpen, setIsOpen] = useState(false);
const { pathname } = useLocation();
useEffect(() =>{
setIsOpen(false);
}, [pathname])
return (
<div>
<button onClick={() => setIsOpen(prev => !prev)}>Toggle DropDown</button>
{isOpen && <DropDown close={() => setIsOpen(false)} />}
</div>
);
}
function Page1() {
return <h1>Page 1</h1>;
}
function Page2() {
return <h1>Page 2</h1>;
}
function App() {
return (
<div className="App">
<BrowserRouter>
<Header />
<Switch>
<Route exact path="/" component={Page1} />
<Route path="/page2" component={Page2} />
</Switch>
</BrowserRouter>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Try to subscribe on current location like this:
const location = useLocation();
useEffect(() => {
closeHeader();
}, [location]);
This one is good I guess ;3

Categories

Resources