How to find Popup state and refresh state in React-router? - javascript

I know this question kind a stupid But I am pretty confused with this site producthunt how they are doing this.When clicking the product list popup with react router is done like this..
But When I refresh that page it render like this..How this is done using React-router

My bet would be that they use the state property when pushing a page to give an indication to the component about how to render the page. More specifically, to indicate the component where it comes from. For example:
router.push({
pathname: '/posts/origami-studio-by-facebook',
state: { fromPosts: true }
})
And then you can read the router's state in the route's component to check what page to show.
const Post = (productName) => {
if(this.context.router.location.state.fromPosts) {
return <Posts productPopup{productName} />
// open the posts page with a popup for the product
} else {
return <PostPage productName={productName} />
}
}
So when you open the page in your browser, the state.fromPosts is not set and you get redirected to the PostPage. In the end, even if the route is the same, what you end up seing is completely different.

Related

Router.push or Link not rendering/refreshing the page even thought the url is updated nextjs

I apologize for my horrendous way of explaining my issue. I have shared a link below description which is exactly the same issue I am experiencing. Any kind of help would be greatly appreciated.
I have directory path like pages/request/[reqid].js . When my url is localhost:3000/xyz and I navigate to pages/request/1 by clicking a button on the current page, the page successfully loads the page with proper data from [reqid=1] but when I try to access pages/request/[reqid].js with different reqid (say suppose reqid=2), the url reflects the correct the reqid pages/request/2 but the page remains the same, doesn't change. However if I go back to other pages like localhost:3000/xyz and click a button there to navigate to pages/request/2 it works but from within pages/request/[reqid] it doesn't render a page associated to the corresponding reqid even thought the url is updated. I have tried both Link and router.push ,both fails to render the correct reqid page.
https://github.com/vercel/next.js/issues/26270
It actually failed to include that I was using getServerProps to fetch the data, which was the reason the page wasn't rendering , unless the page was manually refreshed. The page state is not reset for navigation between dynamic routes that served by the same source component.
for example, give page source /a/[param]/index.js, when navigating from /test/123 to /test/124, states on the page wasn't being reset.
So actually happened is the same React Component been rendered with different props. Thus react takes it as a component is rerendering itself, and causing the new navigated page receive stale states.
To fix it, just add {key: } to page initial props or getserversideprops
export const getServerSideProps = async (ctx) => {
try {
const { reqid } = ctx.params;
//fetch code
return {
props: {
key: reqid,
data:data
},
};
} catch (error) {
console.log(error);
}
};

React: change url without rerender; using window.history?

I have a "settings" page in my react app. The page has several tabs rendering different parts of settings.
It would be better UX if a user can share urls with other users.
What I want is (inside "settings" page):
user A clicks a tab
url changes with a #tabname appended
user A send that url to user B, and user B open that url
user B sees the same tab as user A
But with react router, the whole page re-renders if the url changed:
import { withRouter } from "react-router-dom"
const MyComp = (props) => {
...
const onTabChange = () => {
// append #tabname here
props.history.replace(...); // or `push`
...
}
...
export default withRouter(MyComp)
}
After a lot of searches, I found a solution to use window.history:
const onTabChange = () => {
window.history.pushState(null, null, "#tabname");
...
}
This does the trick, but little information and explanation, and I'd love to know the consequences of using this trick.
Is this a valid solution (for a react app)? Will this cause any problem?
(PS. I know how to parse a url)
More details:
To be more specific, there is a AuthChecker wrapper for all pages. When react router's location changes, it checks for the route's allowed auths and current user's auth.
I've tried /path/:id and everything but all change location, so auth checked and page rerendered.
And I've given up a solution in react router and just want to know: is it safe to change url with window.history in a react app using react router to manage routes?
this question is already answerd at this post.
so it says window has a property called history and there is a method on history which helps you update the history state without react-router-dom understanding it.
like this:
window.history.replaceState(null, 'New Page Title', '/new_url');

how to re render component on Link click [duplicate]

Is there a way to force a React-Router <Link> to load a page from path, even when the current location is already that page? I can't seem to find any mention of this in the react-router documentations.
We have a page on a route for "apply" that loads up a landing page with a hero image, some explanatory text, etc., and an "apply for this program" button that swaps in content that acts as an application form. This all happens on the same "apply" route, because users should not be able to directly navigate to this form without first hitting the landing page.
However, when they have this form open, and they click on the apply link in the nav menu again, the entire page should reload as it would on first mount, getting them "back" (but really, forward) to the landing page again.
Instead, clicking the <Link> does nothing, because react-router sees we're already on the "apply" page, and so does not unmount the current page to then mount a different one.
Is there a way to force it to unmount the current page before then mounting the requested page, even if it's for the page users are supposedly already on? (via a <Link> property for instance?)
Note: this question was posted when React-Router meant v5, and while the problem in this post is independent of a specific React-Router versions, but the solutions are not. As such, the accepted answer is the solution for React-Router v6, so if you're still using v5, first and foremost upgrade your version of React-Router, but if you absolutely can't, the accepted answer won't work for you and you'll want this answer instead.
In the Route component, specify a random key.
<Route path={YOURPATH} render={(props) => <YourComp {...props} keyProp={someValue} key={randomGen()}/>} />
when react see a different key, they will trigger rerender.
A fix I used to solve my little need around this was to change the location that React-Router looks at. If it sees a location that we're already on (as in your example) it won't do anything, but by using a location object and changing that, rather than using a plain string path, React-Router will "navigate" to the new location, even if the path looks the same.
You can do this by setting a key that's different from the current key (similar to how React's render relies on key) with a state property that allows you to write clear code around what you wanted to do:
render() {
const linkTarget = {
pathname: "/page",
key: uuid(), // we could use Math.random, but that's not guaranteed unique.
state: {
applied: true
}
};
return (
...
<Link to={linkTarget}>Page</Link>
...
);
}
Note that (confusingly) you tell the Link which values you need pass as a state object, but the link will pass those values on into the component as props. So don't make the mistake of trying to access this.state in the target component!
We can then check for this in the target component's componentDidUpdate like so:
componentDidUpdate(prevProps, prevState, snapshot) {
// Check to see if the "applied" flag got changed (NOT just "set")
if (this.props.location.state.applied && !prevProps.location.state.applied) {
// Do stuff here
}
}
Simple as:
<Route path="/my/path" render={(props) => <MyComp {...props} key={Date.now()}/>} />
Works fine for me. When targeting to the same path:
this.props.history.push("/my/path");
The page gets reloaded, even if I'm already at /my/path.
Based on official documentation for 'react-router' v6 for Link component
A is an element that lets the user navigate to another page by clicking or tapping on it. In react-router-dom, a renders an accessible element with a real href that points to the resource it's linking to. This means that things like right-clicking a work as you'd expect. You can use to skip client side routing and let the browser handle the transition normally (as if it were an ).
So you can pass reloadDocument to your <Link/> component and it will always refresh the page.
Example
<Link reloadDocument to={linkTo}> myapp.com </Link>
At least works for me!
Not a good solution because it forces a full page refresh and throws an error, but you can call forceUpdate() using an onClick handler like:
<Link onClick={this.forceUpdate} to={'/the-page'}>
Click Me
</Link>
All I can say is it works. I'm stuck in a similar issue myself and hope someone else has a better answer!
React router Link not causing component to update within nested routes
This might be a common problem and I was looking for a decent solution to have in my toolbet for next time. React-Router provides some mechanisms to know when an user tries to visit any page even the one they are already.
Reading the location.key hash, it's the perfect approach as it changes every-time the user try to navigate between any page.
componentDidUpdate (prevProps) {
if (prevProps.location.key !== this.props.location.key) {
this.setState({
isFormSubmitted: false,
})
}
}
After setting a new state, the render method is called. In the example, I set the state to default values.
Reference: A location object is never mutated so you can use it in the lifecycle hooks to determine when navigation happens
I solved this by pushing a new route into history, then replacing that route with the current route (or the route you want to refresh). This will trigger react-router to "reload" the route without refreshing the entire page.
<Link onClick={this.reloadRoute()} to={'/route-to-refresh'}>
Click Me
</Link>
let reloadRoute = () => {
router.push({ pathname: '/empty' });
router.replace({ pathname: '/route-to-refresh' });
}
React router works by using your browser history to navigate without reloading the entire page. If you force a route into the history react router will detect this and reload the route. It is important to replace the empty route so that your back button does not take you to the empty route after you push it in.
According to react-router it looks like the react router library does not support this functionality and probably never will, so you have to force the refresh in a hacky way.
I got this working in a slightly different way that #peiti-li's answer, in react-router-dom v5.1.2, because in my case, my page got stuck in an infinite render loop after attempting their solution.
Following is what I did.
<Route
path="/mypath"
render={(props) => <MyComponent key={props.location.key} />}
/>
Every time a route change happens, the location.key prop changes even if the user is on the same route already. According to react-router-dom docs:
Instead of having a new React element created for you using the
component prop, you can pass in a function to be called when the
location matches. The render prop function has access to all the same
route props (match, location and history) as the component render
prop.
This means that we can use the props.location.key to obtain the changing key when a route change happens. Passing this to the component will make the component re-render every time the key changes.
I found a simple solution.
<BrowserRouter forceRefresh />
This forces a refresh when any links are clicked on. Unfortunately, it is global, so you can't specify which links/pages to refresh only.
From the documentation:
If true the router will use full page refreshes on page navigation. You may want to use this to imitate the way a traditional server-rendered app would work with full page refreshes between page navigation.
Here's a hacky solution that doesn't require updating any downstream components or updating a lot of routes. I really dislike it as I feel like there should be something in react-router that handles this for me.
Basically, if the link is for the current page then on click...
Wait until after the current execution.
Replace the history with /refresh?url=<your url to refresh>.
Have your switch listen for a /refresh route, then have it redirect back to the url specified in the url query parameter.
Code
First in my link component:
function MenuLink({ to, children }) {
const location = useLocation();
const history = useHistory();
const isCurrentPage = () => location.pathname === to;
const handler = isCurrentPage() ? () => {
setTimeout(() => {
if (isCurrentPage()) {
history.replace("/refresh?url=" + encodeURIComponent(to))
}
}, 0);
} : undefined;
return <Link to={to} onClick={handler}>{children}</Link>;
}
Then in my switch:
<Switch>
<Route path="/refresh" render={() => <Redirect to={parseQueryString().url ?? "/"} />} />
{/* ...rest of routes go here... */}
<Switch>
...where parseQueryString() is a function I wrote for getting the query parameters.
There is a much easier way now to achieve this, with the reloadDocument Link prop:
<Link to={linkTarget} reloadDocument={true}>Page</Link>
you can use BrowserRouter forceRefresh={true}
I use react-router-dom 5
Example :
<BrowserRouter forceRefresh={true}>
<Link
to={{pathname: '/otherPage', state: {data: data}}}>
</Link>
</BrowserRouter>
Solved using the Rachita Bansal answer but with the componentDidUpdate instead componentWillReceiveProps
componentDidUpdate(prevProps) {
if (prevProps.location.pathname !== this.props.location.pathname) { window.location.reload();
}
}
You can use the lifecycle method - componentWillReceiveProps
When you click on the link, the key of the location props is updated. So, you can do a workaround, something like below,
/**
* #param {object} nextProps new properties
*/
componentWillReceiveProps = (nextProps)=> {
if (nextProps.location.pathname !== this.props.location.pathname) {
window.location.reload();
}
};
To be honest, none of these are really "thinking React". For those that land on this question, a better alternative that accomplishes the same task is to use component state.
Set the state on the routed component to a boolean or something that you can track:
this.state = {
isLandingPage: true // or some other tracking value
};
When you want to go to the next route, just update the state and have your render method load in the desired component.
Try just using an anchor tag a href link. Use target="_self" in the tag to force the page to rerender fully.

React & MobX - Confirmation dialog when a user navigates away from the existing page

I have something that looks like this:
import React from 'react';
import PropTypes from 'prop-types';
import { Prompt } from 'react-router-dom';
const ConfirmationDialog = (props) => {
if (props.navigatingAway) {
window.onbeforeunload = () => true;
} else {
window.onbeforeunload = null;
}
return (
<Prompt
when={props.navigatingAway}
message="Are you sure?"
/>
);
};
ConfirmationDialog.propTypes = {
navigatingAway: PropTypes.bool.isRequired,
};
export default ConfirmationDialog;
I'm trying to figure out the best way to extend this so that navigatingAway actually does something. I don't understand what criteria to use for it, necessarily, just that it should trigger the confirmation window when:
a user changes the URL and attempts to navigate away
a user clicks on a link
a user refreshes the browser
What would be the best way to check for URL changes for when?
You don't need to come up with a way to 'detect' when one of your scenarios is occurring.
a user changes the URL and attempts to navigate away
a user refreshes the browser
These are already handled by virtue of assigning a callback to onbeforeunload.
a user clicks on a link
This is already handled by virtue of Prompt being rendered, if you're handling navigation with react-router.
props.navigatingAway, then, would be better named props.shouldPreventNavigation or something along those lines, because it should signal IF you should prevent navigating, not whether you ARE navigating.
For example, if you ALWAYS want a prompt to appear before navigation while ConfirmationDialog is mounted, then props.shouldPreventNavigation should just always be true, and you're done. A common use case would be to set it to true if there is unsaved data in a form.
From the docs for Prompt:
Instead of conditionally rendering a <Prompt> behind a guard, you can always render it but pass when={true} or when={false} to prevent or allow navigation accordingly.
To illustrate this, the following two snippets are functionally equivalent, apart from performance and such:
render() {
return (
<Prompt
when={this.props.navigatingAway}
message="Are you sure?"
/>
)
}
render() {
if (this.props.navigatingAway) {
return (
<Prompt
when={true}
message="Are you sure?"
/>
)
}
return null;
}
If Prompt isn't working properly out of the box when when={true}, then it could be that your routing isn't being properly managed by react-router.
As a side note, make sure you consider what happens with window.onbeforeunload if, for example, your ConfirmationDialog unmounts while it has a callback assigned. Use the appropriate lifecycle methods to manage this, or things are gonna get weird when you're testing this.

Mithril component not updating when route changes

I am creating my personal website/blog as a a single page application using Mithril.js. All pages and blog posts on my website are rendered using Page and Post components, and the correct page is loaded based on the :slug in the URL.
The problem I have is that whenever I try and switch between pages, the content of the page does not update. Switching between pages and posts works because I am alternating between Page and Post components. But when I try and use the same component twice in a row, going from page to page, it doesn't update the webpage.
m.route(document.body, '/', {
// `Home` is a wrapper around `Page`
// so I can route to `/` instead of `/home`
'/': Home,
'/:slug': Page,
'/blog/:slug': Post
});
const Home = {
view() {
return m(Page, { slug: 'home' });
}
};
Here is the Page component (the Post component is very similar). Both components render correctly.
const Page = {
content: {},
oninit(vnode) {
m.request({
method: 'GET',
url: 'content.json',
}).then((response) => {
Page.content = response.pages[vnode.attrs.slug];
});
},
view() {
if (Page.content) {
return [
m('#content', m.trust(Page.content.body))
];
}
}
};
Why isn't Mithril recognizing that the slug changed?
The docs page for m.route has a solution for you.
When a user navigates from a parameterized route to the same route with a different parameter (e.g. going from /page/1 to /page/2 given a route /page/:id, the component would not be recreated from scratch since both routes resolve to the same component, and thus result in a virtual dom in-place diff. This has the side-effect of triggering the onupdate hook, rather than oninit/oncreate. However, it's relatively common for a developer to want to synchronize the recreation of the component to the route change event.

Categories