Futher to my last question here, I have been trying to map the refs to other routes. The scroll handler is working but ref.current is null. So I am looking for an answer to this dilema. Using no external dependencies, how can I fix this issue?
App.tsx
import React, { useEffect, useRef } from "react";
import { BrowserRouter, Route, NavLink, useLocation } from "react-router-dom";
import Home from "./pages/Home";
import "./styles.css";
const Header = ({ refs }) => {
const location = useLocation();
useEffect(() => {
console.log("location", location.pathname);
switch (location.pathname) {
case "/about":
scrollSmoothHandler(refs.aboutRef);
break;
case "/contact":
scrollSmoothHandler(refs.contactRef);
break;
case "/hero":
scrollSmoothHandler(refs.heroRef);
break;
default:
scrollSmoothHandler(refs.homeRef);
break;
}
}, [location, refs]);
const scrollSmoothHandler = ref => {
console.log("Triggered.");
console.log(ref.current);
//ref.current.scrollIntoView({ behavior: "smooth" });
};
return (
<>
<NavLink to="/hero" activeClassName="selected">
Hero
</NavLink>
<NavLink to="/about" activeClassName="selected">
About
</NavLink>
<NavLink to="/contact" activeClassName="selected">
Contact
</NavLink>
</>
);
};
function App() {
const homeRef = useRef(null);
const heroRef = useRef(null);
const aboutRef = useRef(null);
const contactRef = useRef(null);
return (
<div ref={homeRef} className="App">
<BrowserRouter>
<Header refs={{ aboutRef, contactRef, heroRef, homeRef }} />
<Route
exact
to="/"
refs={{ aboutRef, contactRef, heroRef, homeRef }}
component={Home}
/>
// More routes here.
</BrowserRouter>
</div>
);
}
export default App;
Home.tsx
import React, { Fragment, forwardRef, useRef } from "react";
import "../styles.css";
const Hero = forwardRef((props, ref) => {
return (
<section ref={ref}>
<h1>Hero Section</h1>
</section>
);
});
const About = forwardRef((props, ref) => {
return (
<section ref={ref}>
<h1>About Section</h1>
</section>
);
});
const Contact = forwardRef((props, ref) => {
return (
<section ref={ref}>
<h1>Contact Section</h1>
</section>
);
});
function Home(refs) {
const heroRef = useRef(refs.heroRef);
const aboutRef = useRef(refs.aboutRef);
const contactRef = useRef(refs.contactRef);
return (
<Fragment>
<Hero ref={heroRef} />
<About ref={aboutRef} />
<Contact ref={contactRef} />
</Fragment>
);
}
export default Home;
You can find a link to my Code Sandbox: here. Forks are much appreciated.
You cannot pass refs as props to other components with the name prop without using forwardRef on the commponent. You need to assign another name to it in order for it to work, For example innerRefs.
Also to pass on refs as prop to the Route component, make use of render prop method
App.tsx
import React, { useEffect, useRef } from "react";
import { BrowserRouter, Route, NavLink, useLocation } from "react-router-dom";
import Home from "./pages/Home";
import "./styles.css";
const Header = ({ innerRefs }) => {
const location = useLocation();
useEffect(() => {
console.log("location", location.pathname);
switch (location.pathname) {
case "/about":
scrollSmoothHandler(innerRefs.aboutRef);
break;
case "/contact":
scrollSmoothHandler(innerRefs.contactRef);
break;
case "/hero":
scrollSmoothHandler(innerRefs.heroRef);
break;
default:
scrollSmoothHandler(innerRefs.homeRef);
break;
}
}, [location, innerRefs]);
const scrollSmoothHandler = innerRef => {
console.log("Triggered.");
console.log(innerRef.current);
innerRef.current.scrollIntoView({ behavior: "smooth" });
};
return (
<>
<NavLink to="/hero" activeClassName="selected">
Hero
</NavLink>
<NavLink to="/about" activeClassName="selected">
About
</NavLink>
<NavLink to="/contact" activeClassName="selected">
Contact
</NavLink>
</>
);
};
function App() {
const homeRef = useRef(null);
const heroRef = useRef(null);
const aboutRef = useRef(null);
const contactRef = useRef(null);
return (
<div ref={homeRef} className="App">
<BrowserRouter>
<Header innerRefs={{ aboutRef, contactRef, heroRef, homeRef }} />
<Route
exact
to="/"
render={routeProps => (
<Home
{...routeProps}
innerRefs={{ aboutRef, contactRef, heroRef, homeRef }}
/>
)}
/>
// More routes here.
</BrowserRouter>
</div>
);
}
export default App;
Home.tsx
import React, { Fragment, forwardRef, useRef } from "react";
import "../styles.css";
const Hero = forwardRef((props, ref) => {
return (
<section ref={ref}>
<h1>Hero Section</h1>
</section>
);
});
const About = forwardRef((props, ref) => {
return (
<section ref={ref}>
<h1>About Section</h1>
</section>
);
});
const Contact = forwardRef((props, ref) => {
return (
<section ref={ref}>
<h1>Contact Section</h1>
</section>
);
});
function Home({ innerRefs }) {
return (
<Fragment>
<Hero ref={innerRefs.heroRef} />
<About ref={innerRefs.aboutRef} />
<Contact ref={innerRefs.contactRef} />
</Fragment>
);
}
export default Home;
Working demo here
Related
I been stuck in this for a few days now, i cant find the way to pass the data dinamically from my SlideShow component to a Detail component, I used this as a way to display and filter the category within the same component.
import React, {useState} from 'react'
import styles from '../slideshow/SlideShow.module.css'
import BackToTopButton from '../BackToTopButton'
import { portafolio } from "../../data/dummydata"
import {
generatePath,
useNavigate
} from "react-router-dom";
const allCategory = [...new Set(portafolio.map((item) => item.category))]
const SlideShow = () => {
const [list, setList] = useState(portafolio)
const [category] = useState(allCategory)
const filterItems = (category) => {
const newItems = portafolio.filter((item) => item.category === category)
setList(newItems)
}
const [id, setId] = useState();
const navigate = useNavigate();
const handleProceed = (e) => {
id && navigate(generatePath("/project/:id", { id, filterItems }));
};
return (
<>
<div name='top' className={styles.subnav}>
<div className={styles.var}>
<ul className={styles.menu}>
{category.map((category) => (
<li>
<button onClick={() => filterItems(category)}>{category}</button>
</li>
))}
</ul>
</div>
</div>
<div className={styles.slideshow}>
<div className={styles.container}>
{list.map((items, i) => (
<div>
<a href={`/project/${id}`} key={i} onClick={(e) => {
setId(items.id);
}}>
<img onClick={handleProceed} src={items.cover} alt='' />
</a>
<div className={styles.text}>
<h5>{items.shortdes}</h5>
</div>
</div>
))}
</div>
<BackToTopButton />
</div>
</>
)
}
export default SlideShow
and here is the component where i want to display it, but it only passes the id parameter but not the rest of the data
import React from 'react'
import NavBar from '../navbar/NavBar'
import Footer from '../footer/Footer'
import styles from "../projectinfo/Description.module.css"
import { useParams } from 'react-router-dom'
const Projectinfo = () => {
const { id } = useParams();
console.log(id);
return (
<div >
<NavBar />
<div className={styles.Description}>
<div>
<div className={styles.main}>
<h2>{id}</h2>
<h4>{id.description}</h4>
</div>
<div className={styles.slideshow}>
<img src={id.cover} alt='' />
</div>
<h4>{id.shortdes}</h4>
</div>
</div>
<Footer />
</div>
)
}
export default Projectinfo
and here is the index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Info from './Info';
import Menu from './Menu';
import ProjectInfo from './components/projectinfo/Projectinfo';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<Routes>
<Route path='/' exact element={<Menu />} />
<Route path="/project/:id" element={<ProjectInfo />} />
<Route path='/info' element={<Info />} />
</Routes>
</BrowserRouter>
);
This question already has an answer here:
Why I receive blank page? React
(1 answer)
Closed 11 months ago.
I've used BrowserRouter here, and wrapped the within a tag.
But the problem is, the ShowTodoList page is not rendering.
Is this syntactically correct?
App.js file
import {BrowserRouter, Route, Routes} from 'react-router-dom';
import React, { lazy } from 'react';
import "./App.scss";
const ShowTodoList = lazy(() => import("./components/showTodoList"));
const CreateTodo = lazy(() => import("./components/createTodo"));
function App() {
return (
<div className="app-contents">
TODO - LIST
<BrowserRouter>
<Routes>
<Route path="/" component={ShowTodoList} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
ShowTodoList.jsx
import { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import { UpdateTodo } from "./updateTodo";
function TodoCard({ data, handleEdit, handleDelete }) {
const { _id, title, description } = data;
return (
<li key={_id}>
<div className="title-description">
<h3>{title}</h3>
<p>{description}</p>
</div>
<div className="button-container">
<button className="button" name={_id} onClick={handleEdit}>
edit
</button>
<button className="button" name={_id} onClick={handleDelete}>
delete
</button>
</div>
</li>
);
}
export function ShowTodoList() {
const [todo, setTodo] = useState([]);
const [open, setOpen] = useState(false);
const [id, setId] = useState("");
const [update, setUpdate] = useState(false);
useEffect(
function () {
axios
.get("http://localhost:8000/api/todo")
.then((res) => {
console.log(res.data);
setTodo(res.data);
})
.catch((err) => {
console.log(err.message);
});
},
[update]
);
function handleEdit(e) {
setId(e.target.name);
setOpen(true);
}
function handleUpdate() {
console.log("update:", update, !update);
setUpdate(!update);
}
function handleDelete(e) {
axios.delete(`http://localhost:8000/api/todo/${e.target.name}`);
setTodo((data) => {
return data.filter((todo) => todo._id !== e.target.name);
});
}
function handleClose() {
setId("");
setOpen(false);
}
return (
<section className="container">
<Link to="/create-todo" className="button-new">
<button className="button">New</button>
</Link>
<section className="contents">
<h1>TODO</h1>
<ul className="list-container">
{todo.map((data) => (
<TodoCard
data={data}
handleEdit={handleEdit}
handleDelete={handleDelete}
/>
))}
</ul>
</section>
{open ? (
<section className="update-container">
<div className="update-contents">
<p onClick={handleClose} className="close">
×
</p>
<UpdateTodo
_id={id}
handleClose={handleClose}
handleUpdate={handleUpdate}
/>
</div>
</section>
) : (
""
)}
</section>
);
}
I'm getting a blank page as below:enter image description here
Could anybody please check the codes and point out where I'm going wrong?
If you are using react-router version 6, we don't have component props in this version, and you should use element instead of it.
import { BrowserRouter, Route, Routes } from "react-router-dom";
import React, { lazy, Suspense } from "react";
const ShowTodoList = lazy(() => import("./components/showTodoList"));
const CreateTodo = lazy(() => import("./components/createTodo"));
function App() {
return (
<div className="app-contents">
TODO - LIST
<Suspense fallback={<div>Loading...</div>}>
<BrowserRouter>
<Routes>
<Route path="/" element={<ShowTodoList />} />
</Routes>
</BrowserRouter>
</Suspense>
</div>
);
}
export default App;
I have this serch.js file. When I search and click on the li in the search result, I want to get redirected to <InnerDetail /> but the URL doesn't change or I don't get redirected to this page
but manualy if I type in the URL localhost/detiled/8 I am redirected to to <InnerDetail /> with id as 8
import React from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearch } from "#fortawesome/free-solid-svg-icons";
import { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import { useHistory } from "react-router-dom";
const initialState = {
idaddProducts: "",
};
const Searchclients = () => {
const history = useHistory();
const [showResults, setShowResults] = React.useState(true);
const [poName, pnName] = React.useState(initialState);
const [showSerch, setShowSerch] = React.useState([]);
const [detail, setDetail] = useState(false);
const [inputValue, setInputValue] = React.useState("");
const [filteredSuggestions, setFilteredSuggestions] = React.useState([]);
const [selectedSuggestion, setSelectedSuggestion] = React.useState(0);
const [displaySuggestions, setDisplaySuggestions] = React.useState(false);
const suggestions = [];
showSerch.forEach(function (data) {
suggestions.push(data);
});
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
// console.log(props);
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
// console.log(suggestions);
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<Link to={`/detiled/${suggestion.id}`}> //this link dont work
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</Link>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("all-doctors-list/")
.then((res) => {
const data = res.data;
// pnName(data.data);
// var stringdata = data;
setShowSerch(data);
//console.log(stringdata);
});
// setShowSerch(data);
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
export default Searchclients;
nav.js
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import InnerDetail from "./client/doctor/components/innerto_detail.js";
class Navigator extends React.Component {
render() {
return (
<Router>
<div>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/detiled/:id">
<InnerDetail />
</Route>
</div>
</Router>
);
}
}
function Home() {
return (
<div style={{ paddingTop: "20%", textAlign: "center" }}>
<h1>Home</h1>
</div>
);
}
export default Navigator;
You are missing closing tag of Switch:
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import InnerDetail from "./client/doctor/components/innerto_detail.js";
class Navigator extends React.Component {
render() {
return (
<Router>
<div>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/detiled/:id">
<InnerDetail />
</Route>
</Switch>
</div>
</Router>
);
}
}
function Home() {
return (
<div style={{ paddingTop: "20%", textAlign: "center" }}>
<h1>Home</h1>
</div>
);
}
export default Navigator;
Also its a tipo but you may want to say detailed, instead of detiled, but that does not affect.
Before I used react-router-dom and I hadn't any problem and I changed my route without any problem.
But now I bring hook inside of my project and I got a problem.
When I use <NavLink>, my route changes but it does not render anything from my component. When I refresh my page, the component will appear.
My App.js:
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
const routes={
route: `/main/symbol/:title/:id`,
exact: true,
component: Symbol,
},
{
route: `/main/symbolDetails/:title/:id`,
exact: true,
component: SymbolDetails,
},
render(){
<Router>
<Switch>
{routes.map((route, k) => (
<Route
key={k}
exact={route.exact}
path={route.route}
component={route.component}
/>
))}
</Switch>
</Router>
}
My Home.js:
(in this component I use navlink for changing my page)
import GridContainer from "../../../components/Grid/GridContainer.js";
import "perfect-scrollbar/css/perfect-scrollbar.css";
// #material-ui/core components
import { makeStyles } from "#material-ui/core/styles";
// core components
import Navbar from "../../../components/Navbars/Navbar.js";
import Sidebar from "../../../components/Sidebar/Sidebar.js";
const useStyles = makeStyles(styles);
export default function Admin({ ...rest }) {
// styles
const classes = useStyles();
const [data, setData] = useState([]);
useEffect(() => getSymbolGroup(), []);
const getSymbolGroup = async () => {
let { data } = await symbolGroup.getSymbolGroup();
setData(data.data);
// console.log("data", data);
};
return (
<div className={classes.wrapper}>
<Sidebar
logoText={"Creative Tim"}
logo={logo}
color={color}
{...rest}
/>
<div className={classes.mainPanel}>
<Navbar
/>
<div className={classes.content}>
<div className={classes.container}>
<GridContainer>
{data &&
data.length &&
data.map((x, key) => {
return (
<div className="Subscrip Bshadow ">
<NavLink
to={`/main/symbol/${x.title}/${x.id}`}
className="a rightanime display awidth flexd"
exact
>
<div className="">
<div className="iconpro display">
<img
className="imgwidth "
src={`http://api.atahlil.com/Core/Download/${x.fileId}`}
/>
</div>
</div>
<div className="">
<p style={{ color: "#a3b0c3", width: "100%" }}>
{x.title}
</p>
</div>
</NavLink>
</div>
);
})}
</GridContainer>
</div>
</div>
)}
I realized my problem.
as I say it was correct when I use in class component.
it is not correct because of my useEffect (hook).
I had to use accolade (I mean {}) after use UseEffect in Home.js component.
home.js
useEffect(() => getSymbolGroup(), []); //it is not correct and I need to refresh my page to render
and the way I had to use useEffect is:
useEffect(() => {
getSymbolGroup();
}, []);
// its correct and does not need to refresh page
I am trying to manage session after successful login while redirecting to some page on form submit.
I would do this usually, in a class component:
componentDidMount() {
if (context.token) {
return <Redirect to="/" />
}
}
But I want to use React hooks, therefore; the following code is not redirecting anywhere:
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Switch, Route, Redirect, Link } from "react-router-dom";
es6
const HomePage = props => (
<div>
<h1>Home</h1>
</div>
);
const AboutUsPage = props => {
useEffect(() => {
redirectTo();
}, []);
return (
<div>
<h1>About us</h1>
</div>
);
};
function redirectTo() {
return <Redirect to="/" />;
}
function App() {
return (
<div className="App">
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/us">About us</Link>
</nav>
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/us" component={AboutUsPage} />
</Switch>
</BrowserRouter>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Working sandbox: https://codesandbox.io/s/blue-river-6dvyv?fontsize=14
I have read that if the hook useEffect() returns a function it will only work when the component unmounts. But it should redirect when the component is being mounted.
Any suggestions? Thanks in advance.
You could set redirect variable on the state and based on it redirect in render:
const AboutUsPage = props => {
const [redirect, setRedirect] = useState(false);
useEffect(() => {
setRedirect(true); // Probably need to set redirect based on some condition
}, []);
if (redirect) return redirectTo();
return (
<div>
<h1>About us</h1>
</div>
);
};
You could have it so that the component selectively renders the page based on whether or not the page is given a token.
const AboutUsPage = ({token}) => (
token ? (
<Redirect to="/" />
) : (
<div>
<h1>About us</h1>
</div>
)
);
However, if you would still like to use context when implementing this with React Hooks you can read up on how to use context with hooks in this article. It shows you how you can incorporate context into React with only a few lines of code.
import React, {createContext, useContext, useReducer} from 'react';
export const StateContext = createContext();
export const StateProvider = ({reducer, initialState, children}) =>(
<StateContext.Provider value={useReducer(reducer, initialState)}>
{children}
</StateContext.Provider>
);
export const useStateValue = () => useContext(StateContext);
Done with hooks and context, your AboutUsPage component would resemble something like this.
import { useStateValue } from './state';
const AboutUsPage = () => {
const [{token}, dispatch] = useStateValue();
return token ? (
<Redirect to="/" />
) : (
<div>
<h1>About us</h1>
</div>
);
};
import {Redirect, Switch} from "react-router-dom";
and inside Switch....
<Switch>
<Redirect exact from="/" to="/home" />
</Switch>
This solved my issue.