This question already has answers here:
Why useEffect running twice and how to handle it well in React?
(2 answers)
Closed 6 months ago.
I'm new to React and try to create my first SPA with React Router.
I try to do it with a functional components.
For now it's very basic but even now I faced a problem which I cannot resolve - component renders twice every time I use proper routing.
Problem appeared when I started to use useEffect hook to call simple console.log() on component initialisation. I noticed every time I choose Autosearch from a Navbar React Router render proper component but I can see two logs Called! in a browser console. I've tried recompose structure of my HTML but it didn't work.
index.js
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App/>
</BrowserRouter>
</React.StrictMode>
);
App.js
function App() {
return (
<div className="container-background h-auto">
<div className="container-lg p-2">
<Header/>
<Navbar/>
<section className="content p-4">{<ContentDisplay/>}</section>
<Footer/>
</div>
</div>
);
}
Navbar.js
const Navbar = () => {
return (
<React.Fragment>
<nav className="navbar nav-custom">
<span className="navbar-collapse">
<div className="nav">
<NavLink className="nav-item nav-link text-white" to={"/reversed"}>Reversed</NavLink>
<NavLink className="nav-item nav-link text-white" to={"/autosearch"}>AutoSearch</NavLink>
<NavLink className="nav-item nav-link text-white" to={"/about"}>About</NavLink>
</div>
</span>
</nav>
</React.Fragment>
);
}
ContentDisplay.js
const ContentDisplay = () => {
return (
<>
<Routes>
<Route path="/" exact element={<Home/>} />
<Route path="/reversed" exact element={<Reversed />} />
<Route path="/autosearch" exact element={<Autosearch />} />
<Route path="/about" exact element={<About />} />
<Route path="*" element={<ErrorPage />} />
</Routes>
</>
)
}
Autosearch.js
const Autosearch = () => {
useEffect(() => {
console.log("Called!")
}, []);
return(
<>
Autosearch
</>
)
}
The whole view hierarchy in your app is child of StrictMode (index.js)
In dev mode a double rendering is the intended behavior.
This does not affect production mode.
https://reactjs.org/docs/strict-mode.html
Related
I'm using react router for routing in my app, and I can't seem to figure out how to use the useSearchParams hook in order to clear the search params upon navigating to a new route.
I've tried to utilize the cleanup function of useEffect in order to clean up the search params, but it seems to navigate to the previous route upon exiting the component.
The basic code is something like the following:
import * as React from "react";
import { Routes, Route, Outlet, Link, useSearchParams } from "react-router-dom";
export default function App() {
return (
<div>
<h1>Basic Example</h1>
<p>
Lorem ipsum
</p>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="dashboard" element={<Dashboard />} />
<Route path="*" element={<NoMatch />} />
</Route>
</Routes>
</div>
);
}
function Layout() {
return (
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
<li>
<Link to="/nothing-here">Nothing Here</Link>
</li>
</ul>
</nav>
<hr />
<Outlet />
</div>
);
}
function About() {
const [searchParams, setSearchParams] = useSearchParams();
useEffect(() => {
return () => {
searchParams.delete("someParam")
setSearchParams(searchParams); // This will route back to the `About` component
}
}, setSearchParams)
return (
<div>
<h2>About</h2>
</div>
);
}
And I made a demo.
I've managed to overcome this issue by using the following browser native code, though it would still be nice if a more "react-router"-y solution was possible:
useEffect(() => () => {
const url = new URL(window.location);
url.searchParams.remove('key');
window.history.replaceState(null, '', url.toString());
})
I want to add routing to my app but the "Link" I made in a child component doesn't work onClick, but only when I refresh the page. I guess the problem is the way too much nesting but I have no idea how can I solve it.
One mention: I imported BrowserRouter as Router everywhere.
This is the file structure
This is the code spippets that related to my problem:
App component:
function App() {
return (
<Router >
<div className="App">
<Switch>
<Route exact path="/" component={NewFetch} />
<Route path="/cardID/:id" component={Details} /> //The route that doesn't work
</Switch>
</div>
</Router>
NewFetch (Main) component:
<Router> //Tried with <React.Fragment>
...
<Route path={["/cards/:name", "/cards/:filter"]}>
<Filter isLoaded={isLoaded} handleScroll={handleScroll} toScrollTop={toScrollTop} value={value}
scrollPosition={scrollPosition} jumpToTop={jumpToTop} testFilter={testFilter} />
</Route>
</Router>
Card (child 2) component from :
const Card = (props) => {
return (
<div className={props.img ? "card" : "hide"}>
<Link to={`/cardID/id=${props.id}`} > //Link that doesn't connect
<img src={props.img} alt={props.name} />
</Link>
</div>
)
};
So basically I can't connect the "Link" from a hardly nested component.
function App() {
return (
<Router >
<div className="App">
<Switch>
<Route exact path="/" component={NewFetch} />
<Route path="/cardID/:id" component={Details} /> //The route that doesn't work
</Switch>
</div>
</Router
const Card = (props) => {
return (
<div className={props.img ? "card" : "hide"}>
<Link to={`/cardID/id=${props.id}`} > //Link that doesn't connect
<img src={props.img} alt={props.name} />
</Link>
</div>
)
};
Above is your code which might look right but the is a slight bug here:
The bug is in the wrong way you are linking to path="/cardID/:id
What you are to do is in your Card child2 is:
const Card = (props) => {
return (
<div className={props.img ? "card" : "hide"}>
<Link to={`/cardID/${props.id}`} > //Removed **id=....**
<img src={props.img} alt={props.name} />
</Link>
</div>
)
};
This is what you have to understand that when you make a route like so path="/route/:id" the :id is just a placeholder waiting for you to place anything so id is commonly used so your code makes sense and mainly basically you want to route based on id but one could have written :cat for example but that is just a placeholder
I've created an app via create-react-app. I want to use the HashRouter functionality for my app (React Router v4), but it is not working as expected.
Actually I don't know why. I.e. when I click on the contact link, then the URL changes to the route /contacts, but the related component is not being showed. It still shows the previous loaded component. The links are placed in the Navigation component. Here is one example of a Link.
<Link key={"navigation" + bu.name + "Key"}
to="/products"
className="nav-link dropdown-item imageLink animated"
onClick={(e) => this.handleClickBU(e, bu.bu)}>{bu.name}
</Link>
This is my code of App.js:
import React, { Component } from "react";
import {
BrowserRouter as Router,
HashRouter,
Route,
Link,
Switch
} from "react-router-dom";
<HashRouter>
<div>
<Background />
<div id="wrap">
<div id="main" className="container clear-top marginBottom50px main">
<div id="content">
<Navigation
key="navBar"
languagesToShow={this.state.languagesToShow}
currentLanguage={this.state.currentLanguage}
onLanguageChange={this.handleLanguageChange.bind(this)}
onBUChange={this.handleBUChange.bind(this)}
onIndustryChange={this.handleIndustryChange.bind(this)}
onCountryChange={this.handleCountryChange.bind(this)}
/>
<Route
key={"mainPageRoute"}
path={"/"}
exact={true}
render={(routeProps) => (
<MainPage
{...routeProps}
currentLanguage={this.state.currentLanguage}
country={this.state.countryObject}
contacts={this.state.contacts}
onCountryChange={this.handleCountryChange.bind(this)}
/>
)}
/>
<Route
key={"contactRoute"}
path={"/contact"}
exact={true}
render={(routeProps) => (
<ContactList
{...routeProps}
showCountrySelection={true}
currentLanguage={this.state.currentLanguage}
country={this.state.countryObject}
contacts={this.state.contacts}
onCountryChange={this.handleCountryChange.bind(this)}
key={"contactList"}
/>
)}
/>
<Route
key={"productsRoute"}
path={"/products"}
exact={true}
render={(routeProps) => (
<Products
{...routeProps}
ref={this.props.innerRef}
key="products"
isLoading={this.state.isLoading}
country={this.state.countryObject}
currentLanguage={this.state.currentLanguage}
currentBU={this.state.currentBU}
showMainProductGroups={this.state.showMainProductGroups}
showMainProductGroupDetails={
this.state.showMainProductGroupDetails
}
showProductGroupDetails={this.state.showProductGroupDetails}
mainProductGroups={this.state.mainProductGroups}
onShowMainProductGroupDetailsChange={this.handleShowMainProductGroupDetailsChange.bind(
this
)}
onShowProductGroupDetailsChange={this.handleShowProductGroupDetailsChange.bind(
this
)}
onBUChange={this.handleBUChange.bind(this)}
onCountryChange={this.handleCountryChange.bind(this)}
/>
)}
/>
</div>
</div>
</div>
</div>
</HashRouter>;
Can anyone tell me, why the url is updated, but the component not?
UPDATE
I already searched a lot and I found this link on the react training site.
I tried different things, but no one did not solve my problem.
But when I change HashRouter to Router it works fine!!! Why!?!
Can it be an issue / bug of HashRouter?
I'm trying to use routing in two different components. First is the SIDEBAR component where links are there.... and a dynamic SHOWPANE component where other components will be rendered based on the routes provided by the sidebar.
Here is what I've tried...
//The App component
class App extends Component {
render() {
return (
<div className="App">
<div className="app-con">
<Sidebar />
<Showpane />
</div>
</div>
);
}
}
//The sidebar component
const sidebar = props => (
<>
<div className="sb-con">
<BrowserRouter>
<div>
<Link to="/" className="navs">
My Account
</Link>
<Link to="/history" className="navs">
Account History
</Link>
<Link to="/settings" className="navs">
Account Settings
</Link>
<Link to="/" className="navs">
Log out
</Link>
</div>
</BrowserRouter>
</div>
</>
);
//The showpane component
const showpane = props => (
<>
<div className="sp-con">
<BrowserRouter>
<div>
<Route path="/" component={Account} exact />
<Route path="/history" component={Acchistory} />
<Route path="/settings" component={Accset} />
</div>
</BrowserRouter>
</div>
</>
);
I expect that after clicking Links in the sidebar component the showpane component must be rendered.
The BrowserRouter is your applications router, and therefore should sit at the top of your application's heirarchy.
class App extends Component {
render() {
return (
<Router>
<div className="App">
<div className="app-con">
<Sidebar />
<Showpane />
</div>
</div>
</Router>
);
}
}
Your showpane will then look like:
const showpane = props => (
<>
<div className="sp-con">
<div>
<Route path="/" component={Account} exact />
<Route path="/history" component={Acchistory} />
<Route path="/settings" component={Accset} />
</div>
</div>
</>
);
I would also recommend looking at the sidebar example reacttraining's ReactRouter Docs since that is what you seem to be going for.
I'm trying to implement React Router v4 in my create-react-app app and my routes work except for the route with an /:id parameter as it just renders a blank page. I've searched for 2 days and tried implementing the solutions here which says to add <base href="/" /> to the head section of index.html and I've also tried following this Medium guide for simple routing but it still does not work. I don't understand, what is going on?
My code is below, any help is greatly appreciated!
Index.js
ReactDOM.render((
<BrowserRouter>
<App />
</BrowserRouter>),
document.getElementById('root'));
registerServiceWorker();
App.js
class App extends Component {
render() {
return (
<Routes />
)
}
}
Routes.js
export const Routes = () => {
return (
<main>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/movies" component={Search} />
</Switch>
</main>
);
}
Search.js
render() {
let filteredMovies = this.state.movies.filter((movie) => {
return movie.title.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1;
})
return (
<div>
<p>Search Page</p>
<form>
<input type="text" value={this.state.search} onChange={this.updateSearch}/>
</form>
<div>
{filteredMovies.map((movie, idx) =>
<div>
<div key={idx}>
<Link to={`/movies/${movie.videoId}`}>
<img src={movie.image.high.url} height="160px" width="100px" alt=""/>
<p>{movie.title}</p>
<p>{movie.quality}</p>
</Link>
</div>
</div>
)}
</div>
<Switch>
<Route path="/movies/:id" component={Single} />
</Switch>
</div>
)
}
}
export default Search;
Single.js
class Single extends Component {
render() {
return (
<div>
<p>Single Movie...</p>
{this.props.match.params.id}
</div>
)
}
}
export default withRouter(Single);
Every route works except for /movies/:id where it just renders a completely blank page. It doesn't even show the <p>Single Movie...</p>.
You should remove exact from route that corresponds to Search component in your Routes, i. e.:
export const Routes = () => {
return (
<main>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/movies" component={Search} />
</Switch>
</main>
);
}
The explanation is pretty simple: the exact prop means that your component will render only if current route is exactly the same as you specified in the path prop
Update
If you want to render the list of movies only when no :id is specified, you should render your list in Switch:
<Switch>
<Route exact path="/movies" render={props => {
return (
<div>
{filteredMovies.map((movie, idx) =>
<div>
<div key={idx}>
<Link to={`/movies/${movie.videoId}`}>
<img src={movie.image.high.url} height="160px" width="100px" alt=""/>
<p>{movie.title}</p>
<p>{movie.quality}</p>
</Link>
</div>
</div>
)}
</div>
)
} />
<Route path="/movies/:id" component={Single} />
</Switch>