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.
Related
Context
I use a nested routes/layout setup in react-router (v6.8).
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<BasicLayout />}>
<Route path="demo" element={<BasicNestedLayout />}>
<Route path="index" element={<Index />} />
<Route path="about" element={<About />} />
<Route path="topics" element={<Topics />} />
</Route>
</Route>
)
);
The user can click Link components on BasicNestedLayout to switch between the three sub routes (index, about and topic).
Here is a reproducible example: app | stackblitz editor
Problem
When monitoring the app with React Chrome DevTools, it looks like everything on the page does rerender when I click on a Link. This includes BasicLayout, which contains just the App Header and is the parent of BasicNestedLayout where the Link and its dependent content lives. All components are highlighted (devtools option = Highlight updates when components render).
A quick search on StackOverflow reveals that this was already a challenge in react-router v4/5, but the accepted answers suggest to switch route props from component= to render=, which have both been replaced by element= in v6 (doc).
How can one prevent the rerender of BasicLayout when nothing has changed apart from its children?
Alternatively, is it possible that the React Dev Tools is wrong? I noticed that the useEffect hooks, written without dependencies to run at each rerender, don't seem to print their console.log() in the Console more than once.
Related but still different questions: 1, 2, 3
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
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
I have this setup for my routes:
<Container>
<Input />
<Switch>
<Route path='/' exact component={LatestMovies}/>
<Route path='/movie/:id' component={MovieWithDetails}/>
<Route component={PageNotFound}/>
</Switch>
</Container>
1.How would I need to setup the router, so that the Input component would be visible with all other components inside the Switch, except for the PageNotFound component?
I know that I could add the Input component inside the other components manually, but was wondering if there was a better way?
2.Inside the MovieWithDetails component I'm using fetch, where I fetch for a movie with an id, which I get from clicking on a link from the LatestMovies component through the router props.
The link, and any id can also be manually typed, so I was wondering, how would I need to handle the showing of PageNotFound component here?
1 - Did you try with multiple components? Something like:
<Switch>
<Input/>
<Route path='/' exact component={LatestMovies}/>
<Route path='/movie/:id' component={MovieWithDetails}/>
</Switch>
<Switch>
<Route path='/somepath' component={PageNotFound}/>
</Switch>
2 - I think that it's subjective. In my opinion I would show a feedback saying that there's no movie with the typed ID and I would not redirect to the PageNotFound component. But if you want to do it, with a simple `this.props.history.push('/somepath') will be right
I'm trying to make a site that is fully protected behind a login page. Once the user has logged in, they are redirected to the main page, which has a completely different layout.
MainLayout (includes header, sidebar etc.)
LoginLayout (centers everything on the page, but doesn't include the above elements)
FutureLayout (I want to be able to use different layouts for different pages later)
At the moment, the biggest problem I'm facing is that every time I change my route, everything rerenders. I sort of understand why this is happening, but I've been unable to find a way around it.
Here is some of my current progress
First thought
https://codesandbox.io/s/6l10v4o7jn
const HomePage = () => (
<MainLayout>
<h1>HOME PAGE</h1>
</MainLayout>
);
This obviously doesn't work because every time you move to another page, the Pagererenders the Layout. You can see this in action by toggling the green button on either HomePage or AboutPage then switching routes
Second Thought
https://codesandbox.io/s/moj437no58
<Route render={() => (
<MainLayout>
<Route exact path="/" component={HomePage} />
<Route path="/about" component={AboutPage} />
</MainLayout>
)} />
<Route render={() => (
<LoginLayout>
<Route path="/login" component={LoginPage} />
</LoginLayout>
)} />
This fixes the state problem, but of course it renders both layouts because there's noting telling it not to, but the page is still determined by the router, as expected.
In a real application, those routes would be extracted to their own component like <AuthRoutes> and <PublicRoutes>
Why some solutions might not work
I'm also planning on using connectedRouterRedirect from redux-auth-wrapper to deal with protecting my routes, which can be a problem when using some of the suggestions I've found.
This I believe is because how the wrapper works as a HOC in conjuntion with Route's component prop
i.e <Route component={mustBeAuth(Page)} />
Conclusion
I've been stuck with this for a while, and I feel like I'm getting confused with various techniques and suggestions. Hopefully someone here can have a fresh take on it and perhaps help me figure out a solution.