I'm using react-ga for my google analytics, react-router for routing, and my navbar uses push in order to transfer between pages.
Although I have tried to change it to <Link> and it did not solve the problem.
Here's a relevant piece of my app.js file:
function App() {
useEffect(() => {
ReactGA.initialize("<myCode>");
ReactGA.pageview(window.location.pathname + window.location.search);
}, []);
return (
<div className="App">
<Router>
<Switch>
<Route exact path="/" component={Homepage} />
<Route path="/register" component={Register} />
The right page will be updated when I type the address in the address bar of my browser, or even switch the page using the navbar and then hit "refresh". But it would not be updated if I only move pages through the navbar.
I do understand that this issue is most likely because this line:
ReactGA.pageview(window.location.pathname + window.location.search);
Is probably not executed when I move between pages, but not sure what would be the best practice to fix it.
This is a relevant piece of my navbar:
<div
className={styles.navOption}
onClick={() => {
this.movePage("/"); // <--- Calls this.props.history.push
}}
>
home
</div>
You need to put the useEffect in every component you want analytics for, and not in the main app component, since you want different analytics for each module,
and because it is the wrong place to write it since the App component renders only once in this case, so the useEffect you wrote won't happen each route you enter.
Related
I am using React-Router-Dom to navigate between two javascript elements in a create-react-app dapp but when I use it loads the elements on top of the page and just adds the page to the end of the link (localhost/page).
My goal is to have the page reload to the new page on click of the link button with a new background and styling (already done for that page)
App.js Code
<Router>
<s.Screen>
<Navbar />
<Routes>
<Route path="/MyPandas" element={MyPandas} exact></Route>
</Routes>
/*
Rest of Code
*/
</Router>
My other page is just wrapped in a Fragment Component
<Fragment>
/*
Code
*/
</Fragment>
use exact in your Routes e.g : <Route path='/products' exact>
Assuming your links are working correctly, and it seems they are if the URL in the address bar updates to "/MyPandas".
The issue is that the element prop isn't passed a correct value. react-router-dom#6 Route component's element prop takes a ReactNode, a.k.a. JSX, not a reference to a React component. Pass <MyPandas /> instead of MyPandas.
<Route path="/MyPandas" element={<MyPandas />} />
Note: There's no need for the exact prop. Not only does it not exist in the RRDv6 Route component API, but all routes are now always exactly matched.
I have 3 components in nextjs and i want to achieve the below snippet in nextjs
<Route path="/" component={homePage} />
<Route path="/about" component={aboutPage} />
<Route path="/faq" component={faqPage} />
Q1. How can i do the same in nextjs without page refresh? (without react-router)
(Edit : some scholars are suggesting to read the docs but i have read it thoroughly and what i want is to pass a component along with the route)
Is this even possible in next js?
Q2: If i have url as /products?product_id=productid and on refresh if i want the url to be /products (basically i want to remove all params on refresh) What is the best practice to do this?
Thanks in advance
NextJS functions on a convention-based filesystem-based routing. You'd need to place your components in a directory structure that matches the routes you are wanting.
More details here:
https://nextjs.org/docs/routing/introduction
The Next.js docs don't really cover how to change away from <Route> components, however they have a lot of examples as code on how to do most things with Next.js. https://github.com/vercel/next.js/tree/canary/examples/layout-component
The below is what I used as an alternative to the component (there's no direct Next.js alternative).
_app.js
export default function MyApp({ Component, pageProps }) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page)
return getLayout(<Component {...pageProps} />)
}
Any page:
import Layout from '../components/layout'
import Sidebar from '../components/sidebar'
export default function About() {
return (
<section>
<h2>Layout Example (About)</h2>
<p>
This example adds a property <code>getLayout</code> to your page,
allowing you to return a React component for the layout. This allows you
to define the layout on a per-page basis. Since we're returning a
function, we can have complex nested layouts if desired.
</p>
<p>
When navigating between pages, we want to persist page state (input
values, scroll position, etc) for a Single-Page Application (SPA)
experience.
</p>
<p>
This layout pattern will allow for state persistence because the React
component tree is persisted between page transitions. To preserve state,
we need to prevent the React component tree from being discarded between
page transitions.
</p>
<h3>Try It Out</h3>
<p>
To visualize this, try tying in the search input in the{' '}
<code>Sidebar</code> and then changing routes. You'll notice the input
state is persisted.
</p>
</section>
)
}
About.getLayout = function getLayout(page) {
return (
<Layout>
<Sidebar />
{page}
</Layout>
)
}
The main part for the layout that you want to wrap around the pages, components/layout.js:
import Head from 'next/head'
import styles from './layout.module.css'
export default function Layout({ children }) {
return (
<>
<Head>
<title>Layouts Example</title>
</Head>
<main className={styles.main}>{children}</main>
</>
)
}
What's happening is the _app.js wraps all pages inside the declared layout. Each page then defines what layout that page belongs to. The layout then accepts a page as the {children} prop object of which you can then render anywhere in your layout page.
Next uses filesystem based routing, your folder structure should look like
-- pages
-- index.js
-- about/index.js
-- faq/index.js
For the custom component part, make a component that's clickable, on click, use next builtin router to redirect
const router = useRouter();
router.push('/');
I am building a website with the following link structure:
/blog
/blog/post1
/preview
The /blog url lists all the blog posts, the /blog/post1 shows the post in edit view and the preview button in the same edit view links to /preview url that shows the preview of the edited version.
My initial idea was to use a /blog/post1/preview url but with the use of <Switch> it became complicated. It did not work because of the whole exact issue where I had to correctly match three urls that start with the same address. So I switched to /preview. Now, I can get the first to urls working but the /preview renders nothing on the screen. Is it because of where my <Link> and <Route> are defined in my app. I have following structure:
<Switch>
<Route path='/blog'>
<Blog/>
</Route>
<Switch>
in the main app.js
<Route exact path='/blog'>
//div with some helper views like search bar
</Route>
//it is inside map function that renders a card view of many posts, post1 for simplicity
<Route path='/blog/post1'>
<PostEditor/>
</Route>
in the main Post.js component
<Route path='/preview'>
//Content
</Route>
in the main PostEditor.js
The PostEditor.js has the Link and Route tags in the same file but not others. Is it a reason that it renders nothing. When I move the Route tag from PostEditor, to App.js, everything works. Is it a nesting issue or something like that. I tried searching online but did not have much luck with multiple levels of nesting, usually it is two. Any help is greatly appreciated.
I am using react-router 5.2.0 and HashRouter as it works better with github
Update:
I have added a sample sandbox to mimic my scenario. Why is the /preview link not rendering anything in this case? I would like to know what I am doing wrong in this case, cause I feel there is a knowledge gap about something about react-router that I am missing about creating Links and Routes at different level? Additionally, what would be the best alternative to handle what I am currently doing.
Code SandBox
I don't see any route nesting here, but I suspect the issue you are running into is with path specificity and order.
Within the Switch you should order your routes to list more specific paths before less specific paths. Recall that the Switch returns and renders the first match is finds, so by trying to match more specific paths first you avoid accidentally matching a less specific path prefix. In other words, "/blog" is a path prefix for the other sub-routes so you want to match it last.
"/blog/:postId/preview"
"/blog/:postId"
"/blog"
Code:
<Switch>
<Route path='/blog/:postId/preview'>
//Content
</Route>
<Route path='/blog/:postId'>
<PostEditor/>
</Route>
<Route path='/blog'>
<Blog/>
</Route>
<Switch>
Edit
Why is the /preview link not rendering anything in this case? I would
like to know what I am doing wrong in this case, cause I feel there is
a knowledge gap about something about react-router that I am missing
about creating Links and Routes at different level?
The "/preview" link renders nothing because the component rendered on "/blog/post1" (i.e. Post) is now no longer being rendered. In other words, the URL is updated to "/preview" and Post is unmounted. Since you've no "/preview" route in the root nothing is matched and rendered.
Solution based on your added codesandbox
You can certainly nest routes. Here are the changes I suggest to properly nest blog routes.
App.js - Specificity and order matters, place more specific root paths before less specific paths.
<Switch>
<Route path="/blog">
<Blog />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
Blog.js - Render nested routes into another Switch component, again paying attention to path specificity and order. Use the useRouteMatch hook to access the current path and url props, path for nested route matching and url for nested links. Notice the route from Post for the preview is moved here.
const Blog = () => {
const { path, url } = useRouteMatch();
return (
<div className="blog">
<Switch>
<Route path={`${path}/:postId/preview`} exact>
Blog Preview Before Save
<br />
</Route>
<Route path={`${path}/post1`}>
<Post />
</Route>
<Route path={path}>
Blog Search Bar
<br />
<Link to={`${url}/post1`}>Post 1</Link>
</Route>
</Switch>
</div>
);
};
Post.js - Again, access the url from useRouteMatch to construct the nested link to the preview route.
const Post = () => {
const { url } = useRouteMatch();
return (
<div className="post">
<Link to={`${url}/preview`}>Preview</Link>
</div>
);
};
Demo
Note: Whether or not you "nest" Route components doesn't really matter in the grand scheme of things, the overall path structure will be identical. You can just as easily (probably easier in fact) define all your routes at the root level, and this is common and perfectly acceptable. The trade off may be in maintenance if you want to "nest" a sub-route elsewhere, you'll need to "find-and-replace" all the particular intermediate path segments versus the "nesting solution" you only need to update the path root/prefix. The "nesting solution" is more dynamic in this sense.
With route /preview we can not identify which blog we are viewing. Pasting a minimum working route configuration bellow
import React, {useState, useEffect} from 'react'
import {Link, BrowserRouter, Switch, Route, useParams} from 'react-router-dom'
#app.jsx
<Switch>
<Route path="/blog" exact>
<Blogs />
</Route>
<Route path="/blog/:id" exact>
<PostEditor />
</Route>
<Route path="/blog/:id/preview">
<Blog />
</Route>
</Switch>
const Blogs = () => {
return(
<h1>Blog list</h1>
)
}
const Blog = () => {
const {id} = useParams()
return(
<h1>Blog {id}</h1>
)
}
const PostEditor = () => {
const {id} = useParams()
return(
<h1>Edit Blog {id}</h1>
)
}
Update:
As I uderstand, You need to display preview right after editing form without a routing switch. For that a button wich trigger show preview element can be used.
import React, {useState} from "react";
import { Route } from "react-router-dom";
import { HashLink as Link } from "react-router-hash-link";
const Post = () => {
const [showPreview, setShowPreview] = useState(false);
const [editData, setEditData] = useState('Some Post attributes')
return(<div className="post">
{showPreview ? <Preview editData={editData} /> : null}
<button onClick={() => {setShowPreview(true)}}>Preview</button>
</div>)
};
Preview = ({editData}) => {
return(
<h1>{editData}</h1>
)
}
export default Post;
How to make nested routes???
I want initial route to be course/:course_id? after that, when i click on a node I want my url to become course/:course_id?/nodes/:node_id .
I use : "react-router-dom": "^4.2.2"
return (
<Router>
<div id="app-main">
<Header />
<Route path="/course/:course_id?" component = {Content}/>
<Route path="/course/:course_id?/nodes" component = {Content}/>
<Footer />
</div>
</Router>
);
When i click id redirects me to course/nodes and skips :course_id
return(
<div className="paragraph-text-child" onClick={() => this.props.select(chapter)} key={chapter.node_id} >
<Link to="nodes">{chapter.text}</Link>
{this.iterate(chapter.nodes)}
</div>
);
I think you have some concepts mixed up... Route is for handling received URLs, Link is for setting it.
Link does not know about Route, Route does not know about Link. Link sets the URL to what is specified in to. So if your current URL is /course, and to="nodes", the result is /course/nodes. If it was to="0/nodes", the result would be /courses/0/nodes.
Now if I understood correctly, you always want a number between "/courses" and "/nodes", correct?
This can be achieved with Redirect, which comes from react-router-dom too.
If you create the following Route:
<Route path="/courses" render={()=> <Redirect to="/courses/0"/>}/>
And rework the previous route so that course_id is NOT optional
<Route path="/course/:course_id" component={Content}/>
When you navigate to /courses, you will be silently redirected to courses/0. The result is that your Link component with to="nodes" will always redirect to courses/number/nodes - because effectively the courses/ location won't be reachable anymore. Every URL that would not contain a course_id, will be redirected to course_id = 0
Note that these 2 routes should be put in a Switch, and in the correct order, otherwise you will end up redirecting every time...
I have not tested this, but it should do the job:
...
<Header/>
<Switch>
<Route path="/course/:course_id" component={Content}/>
<Route path="/courses" render={()=> <Redirect to="/courses/0"/>}/>
</Switch>
<Footer/>
...
And this should handle /course/course_id
Now, if you want to nest a /course/:course_id/nodes/:node_id Route, that should go into the component rendered by the parent route.
Let's rework our parent Route into this:
<Route path="/course/:course_id" render={(props) => <Content ...props />}/>
What this does is, instead of just rendering the passed component, it renders the component and passed down Router props. Which means that the rendered component will be able to handle routes!
Now, in the Content component:
render() {
return <Route path="/course/:course_id?/nodes/:node_id?" component={NodeContent}/>
}
The last thing we need to do is change the to property of the Link component so that it will redirect to the target node:
<Link to={"nodes/" + chapter.node_id}/>
Does this make sense? I may have missed some gotchas in your code - the idea of what you want to achieve is in here, but you may have to adapt it a bit...
basically im rendering this react component:
<Grid>
<Him className="him"><Link to="/him">Him</Link></Him>
<Her className="her"><Link to="/her">Her</Link></Her>
</Grid>
essentially just 2 boxes with links to him and her.
I have this route that correctly renders my Categories when you click on him.
<Route
path="/him"
render={() => (
<Categories
products={products}
addToBasket={addToBasket}
/>
)}
/>
However it renders this component underneath the him/her components. It might sound silly but how do I get it to render them over the top or instead of it?
I could something where I set the state of the component to visible: false and toggle that but that seems a bit messy.
all the stuff in this tutorial: https://reacttraining.com/react-router/web/example/no-match, seems to have them routing via header that never disappears, whereas I want to click on links in the body of my application and then show different content. any ideas?
You have to wrap the Grid in a Route as well. You could do something like this
<Route path='/' exact component={LinkComponent} />
And somewhere else in your code:
const LinkComponent = () => (
<Grid>
<Him className="him"><Link to="/him">Him</Link></Him>
<Her className="her"><Link to="/her">Her</Link></Her>
</Grid>
);
This way, if you go to the home route, you see the Grid, and when you click on a Link, you go to corresponding route and the Grid will be hidden.
Ok you just wants to replace the entire content when click on /him or /her, so as Xander Berkein said you should render your links into a main component and then assign it to the / main route, y that way you need to use exact prop to just render that first path that matches to the URL, so
<Switch>
<Route exact path={‘/‘} component={<Links/>} />
<Route exact path={‘/him’} component={<HimComponent/> />
<Route exact path={‘/her’} component={<HerComponent /> />
</Switch>
I hope you find it useful.