React Router V6 - NotFound component not working with dynamic parameter/slug? - javascript

I have installed "react-router-dom": "^6.0.0-beta.0" and created some page routes. Facing problems where the Page404 / NotFound component is not working if a page doesn't exist. For some reason when I am using dynamic page/post slug the component NotFound will not work if there is no page/post with this ID.
Is there an option inside React Router which solves this issue?
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import AllPosts from "components/AllPosts";
import SinglePost from "components/SinglePost";
import NotFound from "components/Page404";
const App = () => (
<Router>
<Routes>
<Route element={<AllPosts />} path="/" exact />
<Route element={<SinglePost />} path="/:slug" />
<Route element={<NotFound />} path="*" />
</Routes>
</Router>
);
export default App;

The Routes and Route component can't possibly know ahead of time that some dynamic path, "/someUnsupportedIdValue" for example, isn't a valid path to be handled by SinglePost.
The options you have here are:
Conditionally render some alternative UI in SinglePost, something along the lines of "A page by this ID isn't found".
Check if a page with matching ID exists, conditionally render that page, or a redirect (Navigate component replaced Redirect in v6) to your more generic 404 page (actually, a redirect to any "throw away" path that isn't explicitly handled already will land you on your 404 page). Or you can imperatively redirect with a navigate(to, { replace: true }).

Try to remove exact because was removed from v6 and make sure Page404 is the correct component or create Notfound.jsx
Check if the post not exists then redirect to Notfound page.

Related

React Router Link to same component with different URL not re-rendering

I have a component called Posts that lists all posts in a blog. I have links around the post's usernames which link to the same (Posts) component only with a different URL containing a user id to query the posts with. The problem is that the Posts component will not re-render upon visiting this URL, although the URL in the browser changes accordingly.
Routes in App.js:
<Routes>
<Route path='/' element={<Posts/>}/>
<Route path='/user/:user_id/posts' element={<Posts/>}/>
</Routes>
Links in Posts.js
<Link to={`/user/${post.user.id}/posts`}>
{post.user.username}
</Link>
I have tried various things including trying to add a key to the component but it didn't work. I also saw a "force re-render" code snippet but it was for a previous version of React Router.
How can I force the rendering of the Posts component in React Router v6?
If Posts is rendered by a route that has changing route params
<Routes>
<Route path='/' element={<Posts/>}/>
<Route path='/user/:user_id/posts' element={<Posts/>}/>
</Routes>
then it should "listen" for changes to the route's match params.
In Posts use the useParams hook to access the user_id route match param, and use an useEffect hook with a dependency on user_id to run/rerun any logic.
import { useParams } from 'react-router-dom';
...
const { user_id } = useParams();
useEffect(() => {
if (user_id) {
// do something with user_id
}
}, [user_id]);
If Posts is a class component, then either convert it to a function component so it can use React hooks, or create a wrapper component or a new withRouter replacement to use the hook and inject params as a prop.
I'm not sure but maybe this kind of code would work I think...?
<Routes>
<Route path='/' element={props => <Posts {...props} />}/>
<Route path='/user/:user_id/posts' element={props => <Posts {...props} />}/>
</Routes>
And if it does not works,
maybe just using <a> instead of <Link> might be work.
<a href={`/user/${post.user.id}/posts`}>
{post.user.username}
</a>

I am using react-router-dom for my react.js project and it adds /#/ in the route URL. Can I get rid of it? if yes, how?

I am using Switch and Route from react-router-dom in my reactJS project. And it automatically adds # at the end of the route URLs.
example, if I navigate to localhost:3000 it will show the URL as localhost:3000/#/ .
I want to get rid of this # as I am developing a customer facing product and don't want my URLs to have # in it.
Is there any way for removing that "#"?Help is much appreciated.
Thanks in advance.
Edit:
import { Switch, Route, withRouter} from 'react-router-dom';
#inject('userStore', 'commonStore', 'authStore')
#withRouter
#observer
export default class App extends React.Component {
render(){
return(
<Switch >
<Route path="/signin" component={Login} />
</Switch >
)
}
}
It sounds like you are using a HashRouter component. If you replace this with the BrowserRouter component you will get normal URLs.
Adding the code from your root app.js file to your question will help us debug further.
Edit:
Since you aren't specifying a router component, it react-router is defaulting to HashRouter. You need to wrap your whole app component in a BrowserRouter component to get good urls:
import { Switch, Route, BrowserRouter } from 'react-router-dom';
render(){
return (
<BrowserRouter>
<Switch >
<Route path="/signin" component={Login} />
</Switch>
</BrowserRouter>
);
}

React-Router Redirect not working at all, using React-Redux-Electron

I am trying to conditionally redirect to other pages within a modal using from react-router.
I have implemented withRouter at the bottom of the relevant components as well as in the connect function. I am currently not using the Reducer because I have a switch in a root modal component which catches a type and then renders a component. Below is a snippet from the switch.
case type.componentName
return <Redirect to='/component' />
break;
However, the new component is still not rendering. It is as if Redirect is not being registered at all. My route is below.
<App>
<Switch>
<Route path="/" component={HomePage} />
<Route component={upperComponent}>
<Route path="/modal" component={rootComponent}>
<Route path="/component" component={component} />
</Route>
</Route>
</Switch>
</App>
I was originally rendering pages by modifying the state through a boolean and based upon it, a different component would be rendered. This worked just fine but I would rather have some history from using React-Router when I render new pages. Is there something fundamentally wrong about how I am trying to call Redirect or should I use an entirely different strategy all together?
The code below was requested in a comment. This is in my container component and I have one per component.
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import rootComponent from '../components/rooComponent';
import { withRouter } from 'react-router-dom';
import * as rootComponentlActions from '../actions/rootComponent';
function mapStateToProps(state) {
return {
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(rootComponentActions, dispatch);
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(rootComponent));
I have also tried adding export default withRouter(component); as well in my child component to the root as well as in the root to test it out based upon some examples that I have read in the past. As far as I can tell, it made not difference, good or bad.

Error: <Route> elements are for router configuration only and should not be rendered in react-router v4

Tried to upgrade react-router from 2 to 4 and broke it and now cant render my app.
getting various errors (the most recent is: <Route> elements are for router configuration only and should not be rendered)
I have also had the error where my ./ route renders fine but every other route blows up when I refresh and says Cannot GET /randomRoute
I am creating a react app and my main index.js file (where I include ReactDOM.render) also includes the routes and looks like so:
import React from 'react';
import ReactDOM from 'react-dom';
import { Route } from 'react-router';
import { BrowserRouter as Router, Match, HashRouter } from 'react-router-dom'
import Header from './components/header';
import './index.scss';
class App extends React.Component {
render() {
return (
<Router history={HashRouter}>
<div>
<Route path={"/"} component={Header} />
</div>
</Router>
);
}
}
ReactDOM.render(<App />,
document.getElementById('content'));
why would I be getting that current error and can anyone give me a simple start to the basics I need to include just to get routing working? it worked in version 2 but I wanted to upgrade and now cant get it working again
The problem is that you are specifying history object as a Router type.
From the Documentation
A <Router> that uses the hash portion of the URL (i.e.
window.location.hash) to keep your UI in sync with the URL.
This is similar to what you would do when you specify history as
hashHistory in Router v2.
Also, history object has been seprated into a seprate package from v4 onwards.
You can either make use of BrowserRouter or HashRouter to render your Routes.
Change your Route Configuration to below if you want to use BrowserRouter which is <Router> that uses the HTML5 history API (pushState, replaceState and the popstate event) to keep your UI in sync with the URL.This is similar to what you would do when you specify history as browserHistory in Router v2.
Also you need to import Route from 'react-router-dom'.
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Match, Route} from 'react-router-dom'
import Header from './components/header';
import './index.scss';
class App extends React.Component {
render() {
return (
<Router >
<div>
<Route path={"/"} component={Header} />
</div>
</Router>
);
}
}
Well, in react router v4 the API is different. You have to define it in your index.js file like this,
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<div>
<Switch>
<Route path="/path/one" component={ComponentOne} />
<Route path="/path/two" component={ComponentTwo} />
<Route path="/" component={IndexComponent} />
</Switch>
</div>
</BrowserRouter>
</Provider>
, document.querySelector('.container'));
Make sure the order is important here. Put the most generic one at last. Hope this helps. Happy coding !

Pagination issue using React Router v4.1

I'm migrating a site in ASP.NET MVC to REACT. And for pagination i have created a component in React.
Issue i'm facing is with Routing for the pagination URLs. React Router is not able to detect that the URL is different when i click on a pagination URL
Let me explain:
app.js code:
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, applyMiddleware} from 'redux';
import allReducers from '../reducers/index';
import {Provider} from 'react-redux';
import ReduxPromiseMiddleware from 'redux-promise';
import { BrowserRouter, Route } from 'react-router-dom';
import Main from './main';
import Layout from './layout';
const app = document.getElementById('root');
const store = createStore(allReducers, applyMiddleware(ReduxPromiseMiddleware));
ReactDOM.render(<Provider store={store}>
<BrowserRouter>
<Layout>
<Main/>
</Layout>
</BrowserRouter>
</Provider>
,app);
Main component render:
render(){
return(
<main>
<Switch>
<Route exact path='/' component={HomePage}/>
<Route path='/posts' component={PostsRouter} />
<Route path='/studies' component={StudiesPage} />
</Switch>
</main>
);
}
PostsRouter component:
const PostsRouter = () => (
<Switch>
<Route exact path='/posts' component={PostsPage} />
<Route path='/posts/:page' component={PostsPage} />
</Switch>
);
For both /posts and /posts/2 i need the component to be PostsPage.
Lets say i'm at /home. Now i click a posts link and URL changes to /posts. Now if i click /posts/2 link, nothing happens. React Router doesn't detect that the URL is different.
And a weird thing i noted is that if i change the component:
<Route path='/posts/:page' component={PostsPage} />
to
<Route path='/posts/:page' component={StudiesPage} />
then React Router routes me to StudiesPage component if i click on /posts/2 link when i'm on /posts URL.
May be i'm missing something obvious. But i haven't been able to figure out a way after lots of attempts.
I suspect Sergey's comment was right, that's what my problem ended up being. I was fetching data within componentDidMount() but didn't realise that in order to actually update it with new data when the next page link was clicked, I needed to do the same thing inside componentWillReceiveProps(). You can see my full source here but the biggest key part was this:
componentWillReceiveProps(nextProps) {
this.setState({
loaded: false
});
this.fetchMediaItems(nextProps.match.params.page);
}
componentDidMount() {
this.fetchMediaItems(this.props.match.params.page);
}
componentWillReceiveProps() receives the new properties, including page number, when you click on the link to page 2, so you need to do whatever inside there to update with the new state.

Categories

Resources