Difficulty with blocking and react-router - reactjs

I am running into the issue where the URL is changing but the page is not when using react-router and redux. I have attempted the solutions from the docs involving location and using withRouter (https://reacttraining.com/react-router/web/guides/dealing-with-update-blocking), however, I believe the issue stems from the fact that I am trying to reload the same component and then use the params from the URL to make an API call that then re-renders the component with the retrieved values.
<Route path="/video/:id" exact={true} component={VideoDisplay} />
and then from within the VideoDisplay component:
<Link to={`/video/${props.id}`}>
I duplicated the component, which gets around the issue of trying to link to a route's own component:
<Route path="/video/:id" exact={true} component={VideoDisplay} />
<Route path="/video/test/:id" exact={true} component={VideoDisplayTwo} />
<Link to={`/video/test/${props.id}`}>
<Link to={`/video/${props.id}`}>
This works, but I am looking for a more elegant solution.

Related

What is the correct way to setup an independent Route that is not a part of App.js in React Router Dom?

I'm trying to create an independent Route (not sure if that's the correct term) at BulletinBoard.js where I can use Link to go to Create Bulletin component.
I'm also trying to create another independent Route at BulletinList.js where I can navigate to Edit Bulletin with their respective IDs.
Before this, I tried using useRouteMatch with path and url, but apparently that wasn't the correct way to do it, now I'm told to use useLocation, it does adds the /createbulletin and /editbulletin/id paths behind the current URL, but it doesn't navigate to the component itself.
I've been cracking my head over this for the past 2 days and I still haven't figured out the correct way to do this.
Here is the codesandbox that I've created for reference.
The reason your code didnt navigate to a different component after the url changed is because you didnt use the exact attribute when declaring the route. So its matching /bulletinboard/anything and then it always renders de BulletinBoard component.
You could define all routes at the App.js file like
<Switch>
<Route path="/" component={Home} exact />
<Route path="/bulletinboard" component={BulletinBoard} exact />
<Route path="/bulletinboard/edit/:id" component={EditBulletinBoard} exact />
<Route path="/infohub" component={InfoHub} exact />
<Route component={NotFound} />
</Switch>
Also, check out the useHistory hook
So at the BulletinBoard.js when the user clicks the link
onClick={() => history.push(`/bulletinboard/edit/${id}`)}
Note that the edit route renders a different component that your codesandbox didn't have yet

React Router ignores my PrivateRoute wrapper when navigating using a Link

I'm having a strange issue with React Router. My PrivateRoute wrapper doesn't do its thing when navigating using a Link or NavLink.
Essentially I have something like this:
<BrowserRouter>
<Switch>
<Route exact path="/"><Home /></Route>
<PrivateRoute exact path="/private"><Private /></PrivateRoute>
</Switch>
</BrowserRouter>
PrivateRoute is just a wrapper around Route that checks for authentication.
Now, if I go to /private by typing it in the address bar, PrivateRoute does its job (and redirects to /login, but that doesn't matter).
However, if I use a NavLink in the Home component which has to="/private", React Router routes to the private route even if the user is unauthenticated.
Is there any way I can resolve this reasonably? And why does React Router behave like this and doesn't "go through" all the routes in BrowserRouter each time you navigate using a NavLink?
To solve your problem, you need to add exact or exact="true" on public Route.
If you don't include exact="true", <Route path="/"> refers all sub routes starting with /.
So /private will match / route hence it will render <Home /> component.
Here is updated code
<BrowserRouter>
<Switch>
<Route path="/" exact>
<Home />
</Route>
<PrivateRoute path="/private">
<Private />
</PrivateRoute>
</Switch>
</BrowserRouter>
Here is sample code
Well, this was a quite simple and dumb fix actually.
The problem was that the PrivateRoute component didn't "remount" when changing routes using Links (that is interesting and somehow clever behavior of React Router, I can imagine this improves performance).
Meanwhile, typing a URL into the address bar refreshes the entire page, thus "remounting" all components.
Since the logic for checking auth was in componentDidMount only, it didn't launch if the component wasn't "remounted", therefore creating my issue.
For any possible future visitors, to fix this problem, just move your auth checking logic from componentDidMount somewhere else, and reference it both in componentDidMount and componentDidUpdate. Don't forget to check if props actually changed in componentDidUpdate — otherwise you'll create an infinite loop — like so:
componentDidUpdate(prevProps, prevState) {
// You could also use shallowCompare, but that wasn't needed in my case
if (this.props.requiredPerms !== prevProps.requiredPerms) {
this.checkAuth(); // method with the logic which was in componentDidMount before
}
}

Same route path with different component in React

I need a View component and an Edit component. Depends on conditions, a button needs to go View page or Edit page.
I set a Route like below.
The problem is the paths are the same.
<Route exact path='/users/:user_id' component={Users.Edit} />
<Route exact path='/users/:user_id' component={Users.View} />
In page, I have links like below
(type = "A") ? <Link to="users/1">view</Link> : <Link to="users/1">edit</Link>
For now, the router has same path so the button goes to the Edit component.
Is there any good react way I can call Edit component or View component?
or Do I need to just like below?
<Route exact path='/users/:user_id' component={Users.Edit} />
<Route exact path='/users/:user_id/view' component={Users.View} />
I think this is the right decision
<Route exact path='/users/:user_id' component={Users.Edit} />
<Route exact path='/users/:user_id/view' component={Users.View} />
But usual practice for CRUD to define routes in this way
<Route exact path='/users/' component={Users.List} />
<Route exact path='/users/create' component={Users.Create} />
<Route exact path='/users/:user_id' component={Users.View} />
<Route exact path='/users/edit/:user_id' component={Users.Edit} />
You should do it like you suggest with the /view suffix, but I would suggest combining the /users/:user_id with the Users.View component, and the /users/:user_id/edit with the Users.Edit component.
The reason 1 exact url cannot be handled based on conditions is when you open that url from let's say an e-mail, there is no way of telling which component to render.
There is a way, but it's harder than adding just 2 routes.
You might want to change the view/edit decision to a query param on 1 route, but then you have to create a new wrapper component around Users.Edit and Users.View to handle the decision based on the query param. So your route should look like
<Route exact path='/users/:user_id' component={Users.EditOrViewDecision} />
Next you can create links to that route by passing search to the Link
<Link to={{
pathname: '/users/1',
search: '?view=edit'
}}/>
But this way you still have to manage your decision in the EditOrViewDecision based on the ?view=edit query which component you want to display.

React-router: components don't render unless refreshed

I have a simple App component with Links to a User index and a Cache index (for a geocaching app). My Links render fine, and when clicked they change the path in the address bar, but nothing changes in the DOM until I refresh the page, at which point the page looks the way it should. What's going on here, and what's the conventional way of dealing with it? I am using Redux as well, if that makes any difference. The following is all of the JSX returned by my App component:
<div>
<nav>
<Link to="/caches">Caches</Link>
<Link to="/users">Users</Link>
</nav>
<div>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/users" render={() => <div><UserList users={this.props.users}/></div>}/>
<Route path="/caches" component={CacheList}/>
</Switch>
</div>
</div>
Its a common issue with react-router-4.
use {pure: false} in react-redux connect or use withRouter HOC.
React router 4 does not update view on link, but does on refresh

React router 4 does not update view on link, but does on refresh

I am using the following simple nav code
<Router>
<Switch>
<Route exact path='/dashboard' component={Dashboard} />
<Route path='/dashboard/accounts' component={AccountPage} />
</Switch>
</Router>
<NavLink exact to={'/dashboard'}
disabled={this.props.item.disabled}
activeClassName='active'>
<NavLink exact to={'/dashboard/accounts'}
disabled={this.props.item.disabled}
activeClassName='active'>
The URL changes but the view does not. It does however change when I refresh the page or manually go to that URL.
You can also use the:
import { withRouter } from 'react-router-dom';
And then on your export default, you do like this:
export default withRouter(connect(mapStateToProps, {})(Layout));
Because when you have an export connect, you need to tell that that component will be using the router.
This is because react-redux connect method implements shouldComponentUpdate which will cause component not to render when props didn't change. And this is conflicting now with react-router 4.
To avoid it you can pass {pure: false} to connect as described in react-redux troubleshooting section.
Another way is to use withRouter HOC or pass location prop like described in DOCS.
I had my Navlinks in a stateless-component (or dumb component) and a container to control the collapse-state of my navbar.
after switching the navbar-container from PureComponent to Componentit solved the problem for me.
I have encountered this problem. I resolve it by add attribute key to component Switch with value is a location pathname and location search.
Have you tried making sure that your router tags wrap the entire chunk of code?
<Router>
<Switch>
<Route exact path='/dashboard' component={Dashboard} />
<Route path='/dashboard/accounts' component={AccountPage} />
</Switch>
<NavLink exact to={'/dashboard'}
disabled={this.props.item.disabled}
activeClassName='active'>
<NavLink exact to={'/dashboard/accounts'}
disabled={this.props.item.disabled}
activeClassName='active'>
</Router>
It looks odd, but including links into the <Router> propagates your path change to router components when you click the link and actually renders the component you are routing to. Just fixed a very similar problem myself.

Resources