react-router-dom Prompt never shows - reactjs

I have a React Route with significant user input that I want to use the Prompt component with. I'm importing it from react-router-dom without error and adding the component to the render method of the class component, but no matter what, the prompt alert never actually appears and the user can freely navigate off the page without seeing any alert.
I've tried:
Putting the component in with when={aStateBooleanVariable} as well as when={true} and no when attribute at all.
Using a function for the message.
Ensuring that the Route it's on is an exact path.
Importing it from react-router instead of react-router-dom.
Moving the component around within the render method.
Moving the Prompt into the actual Switch statement itself in the App component that wraps everything in BrowserRouter.
Nothing I've tried has gotten the prompt to actually show up or prevent the user from navigating off the page and I've been unable to find anything in the docs or extensive searching where anyone else has had this problem. Does anyone have any idea why this just isn't even starting to work?
I'm not really sure what code to put since it's all pretty much boilerplate react-router-dom.
This is my App.jsx switch statement:
return (
<BrowserRouter>
<header>
{loggedInUser ? <NavbarComp loggedInUser={loggedInUser} setLoggedInUser={setLoggedInUser} /> : null}
</header>
<Switch>
<PrivateRoute path="/ged/campaigns/new" component={CampaignNew} loggedInUser={loggedInUser} />} />
<PrivateRoute path="/ged/campaigns/:id" component={Campaign} loggedInUser={loggedInUser} />} />
<PrivateRoute path="/ged/characters/new" component={CharGen} loggedInUser={loggedInUser} />} />
<PrivateRoute path="/ged/characters/:id" component={CharacterMain} loggedInUser={loggedInUser} />} />
<PrivateRoute path="/ged" component={GEDHome} loggedInUser={loggedInUser} />} />
<Route path="/" component={Home} loggedInUser={loggedInUser} setLoggedInUser={setLoggedInUser} />} />
<Route component={DeadPage} />
</Switch>
</BrowserRouter>
)
I'm importing the component as:
import {Prompt} from 'react-router-dom';
And incorporating the component at the top of its parent's render method return:
<Prompt message="You have changes that will be lost if you leave without saving." />
What other code might be making it fail silently like this?

Related

Is there such a thing like sass mixins in react?

So I have a react application that involves a lot of routing and this routings are done based on conditions. For example if a user logs in then some of the paths might mean something different than what they would mean if the user wasn't logged in. Here is a simple example.
<Switch>
(this.state.isAuthenticated)?
this.state.game?
<Route path="/game/Game91" render={(props) => <Game91 {...props} games={this.state.allGames} />} />:
<Route path="/game/Game91/" render={(props) => <CreateGame {...props} game="game91" />} />:
<Route exact path="/game/Game91" render={(props) => <LoginForm {...props} onGuestLogin = {this.guestLogin} parentLocation = "/game/Game91" />} />
}
</Switch>
...
So in the above code if a user is authenticated and if the game is choosen then I want the url /game/Game91 to take the user to the Game91 component. If the user is authenticated but a game isnt choosen I want /game/Game91 to take the user to the create game component and so forth. And this has been workign for me for now. I unserstand this might not be the best practice out their, so I would be glad if you share what the best practices are in such scinarios and send me links to more reads. But my main question for now is that what if I have another path like /somepath/to that also means different things like the /game/Game91. To do, that I will have to rewrite all the above code since I can't fit all this in one ternary operator. Hypotetically this is what I would like.
<Switch>
(this.state.isAuthenticated)?
this.state.game?
<Route path="/game/Game91" render={..something..} />
<Route path="/somePath/to" render={..something..} />
:
<Route path="/game/Game91" render={..somethingElse..} />
<Route path="/somePath/to" render={..somethingElse..} />
:
<Route path="/game/Game91" render={..something..} />
<Route path="/somePath/to" render={..something..} />
}
</Switch>
But the above isn't possible because a ternary operators can't return more than one thing. and I cant use if statments beacuse JSX doesn't allow them. So ideally if I could have function that will return this pair of things without wrapping them in any kind of container like you would with scss mixins that would be great. So is there such a thing in react or are there other better ways to do such things?
Generally every page or container should be addressed to only one route path. Instead of changing the component at runtime by checking the flags, you can navigate from one route to another based on your conditions using react hooks.
Note:
This example uses react router v6. If you are using the v5.2 version of react router, then you need to use history api instead of navigate.
useEffect(() => {
if (isAuthenticated) {
if(isGameActive)
navigate('/gaming');
else
navigate('/create-game');
}
else {
navigate('/login');
}
}, [isAuthenticated]);
<Router>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/gaming" element={<Game91 />} />
<Route path="/create-game" element={<CreateGaming />} />
</Routes>
</Router>
react-router v6

Component is rendered before i can redirect the route. How can I mitigate rendering?

I want to route through 3 sites. Login - Dashboard - Details
Details is based on data selection in Dashboard.
If I refresh the url/Details site, the context is lost and i would like to redirect to Dashboard, but Details is throwing erros before i can redirect. How can I change this behaviour?
I've tried the following approaches without success:
1. redirection in the Browserrouter based on if the necessary data exists, if not redirect to Dashboard
2. Within Details componentWillMount() I have set up a redirect
Do I really need to check every data usage if data exists? This seems like quite a hassle for simple routing...
This is my routing configuration. The second redicrect doesn't work.
{this.props.token?<Redirect to="/dashboard" />:<Redirect to="/login" />}
{this.props.selectedCow?<Redirect to="/dashboard"/>:null}
<Switch>
<Route path="/login" component={Login} />
<Route path="/details" component={CowDetails} />
<Route path="/dashboard" exact component={Dashboard} />
</Switch>
Error: Data is not defined (CowDetails is rendered and context is missing before redirection)
I was using Redirect in the wrong way. As specified in the Docs it actually works without rendering the component.
https://reacttraining.com/react-router/web/api/Redirect
So for me it is
<Route exact path="/details" render={() => (
this.props.selectedCow ? (
{CowDetails}
) : (
<Redirect to="/dashboard"/>
)
)}/>

How to render a component on each route with working functionality?

I'm trying to render a component that plays an mp3 on each route, and have the mp3 continue to play uninterrupted despite route changes. I'm not sure if this is possible, but i'd assume it is considering React creates single page sites.
So far i've only gotten as far as getting the component to render, my problem is all of the functionality along with the component has seized to work... I can't figure out why..
<HashRouter>
<Route path="/" render={props => <Music />} />
<Route exact path="/" component={Home} />
<Route path="/work" component={MyWork} />
<Route path="/about" component={About} />
</HashRouter>
The Route in question is the first one.
I've also tried this thinking I needed to pass down the props in order for the functionality to work
<Route path="/" render={props => <Music
changePlayingState={props.changePlayingState}
playing={props.state.playing}
showMusicMessage={props.showMusicMessage}
hideMusicMessage={props.hideMusicMessage}/>} />
A working version of the component(and how i would like it to appear on all routes) is shown on the home page(click play) code sandbox had a file size limit so i just added some popping sound effects(takes a second to start playing)
https://codesandbox.io/s/rlw1q45m1m
The only components regarding this question (and the only you'll need to view)are as follows:
Index.js
The routes: Home.js, About.js, MyWork.js
Music Component: Music.js
If any additional information is needed please ask! Thanks!
You just have to pull out the Music Component and make it a stand alone Component in itself then pass it down to the HashRouter within a single child and rest of the Routes.
Here is an example to implement NavBar in each route of the React App.

React Router v4 rendering component twice

Here is my Router Implementation
<BrowserRouter>
<div>
<Route exact path="/" component={ProfilesIndex} />
<Route exact path="/profiles" component={ProfilesIndex} />
<Route exact path="/profiles/new" component={ProfileEditor} />
<Route exact path="/profiles/:id" component={ProfileEditor} />
</div>
</BrowserRouter>
When I am browsing to /profiles/new path, it is rendering the ProfileEditor component twice. For every other route, it is working fine.
Can someone suggest how to fix this problem ?
I found the answer after browsing into multiple sections in Router Docs. Problem was it was matching multiple routes
When user opens /profiles/new it matches two routes
/routes/new
/routes/:id
Because :id is like a wildcard * and it also matches new word so both routes get matched and hence the component gets rendered twice.
The solution is to wrap the routes with Switch if you do not want to match multiple routes.
<BrowserRouter>
<Switch>
<Route exact path="/" component={ProfilesIndex} />
<Route exact path="/profiles" component={ProfilesIndex} />
<Route exact path="/profiles/new" component={ProfileEditor} />
<Route exact path="/profiles/:id" component={ProfileEditor} />
</Switch>
</BrowserRouter>
For anyone coming here from v6+
exact prop no longer exists, the paths are exact by default if they aren't appended a wildcard *
However I was still getting a double render. I ran a prod build and checked and the double render is gone so probably nothing to worry about - sometimes hooks run twice in development mode (I guess that's what's happening internally)
for me, this is because of React.StrictNode which is wrapped arround App component.
it intentionally double render components (only in development) to enforce you, not use side effects on some of your component's
lifecycle events.
the reason behind that is documented here

Reactjs - React Router disallowing paths

I have a route
<Route exact path="/view/:personID" component={PersonView} />
It works fine, however /view still renders, albeit with no person in it. Is there a way to disallow this path or turn in into a 404?
Answer was to use switch
<Switch>
<Route exact path="/view/:personID" component={PersonView} />
<Redirect from='/view' to='/' />
</Switch>
You can manage this on your component, in the render section test if you have your personID if not return a custom error/view.

Resources