Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Link method which prevent same locations in the history #570

Closed
wants to merge 13 commits into from

Conversation

dabit1
Copy link

@dabit1 dabit1 commented Feb 12, 2018

I created a method that call push when current location and next location is different and call replace when they are the same.

It fixes a known problem which was reported for many users.
#470
#558
#507

Copy link
Contributor

@guidobouman guidobouman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, there's a createLocation method, why are you not using that one?

CHANGES.md Outdated Show resolved Hide resolved
modules/createBrowserHistory.js Outdated Show resolved Hide resolved
modules/createHashHistory.js Outdated Show resolved Hide resolved
Copy link
Contributor

@guidobouman guidobouman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it looks good, but I have some questions that might improve the overall PR a little.

README.md Outdated Show resolved Hide resolved
modules/LocationUtils.js Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
@kujukuju
Copy link

Is there a workaround for this issue? It seems like this has been an issue for the last ~3 years.

@dabit1
Copy link
Author

dabit1 commented Mar 25, 2018

@TrentWDB I did not have much time lately. In max 1week I hope to review it ;)

@guidobouman
Copy link
Contributor

Hey @mjackson, I think @dabit1 has a great PR here.

It fixes the long standing issue with links & history.push in a solid way. Which would in turn allow for a small PR in react-router to finally let the Link component start behaving like a true link. 🎉

@nksfrank
Copy link

nksfrank commented Apr 9, 2018

What's the status on this PR?

@guidobouman
Copy link
Contributor

Waiting for the repo owner @mjackson to merge this, after that a new PR for react-router can be made.

@sabbiu
Copy link

sabbiu commented Apr 16, 2018

I am also looking for this fix. What is the status?

@darklightblue
Copy link

@mjackson This type of fix seems to have been discussed ad nauseam for an incredibly long time; even a couple of PRs have vanished. Would be great to get this fixed, finally.

@quisido
Copy link

quisido commented Apr 30, 2018

Hoping to bring attention to this. I'm also awaiting this fix.

@alonquil
Copy link

alonquil commented May 3, 2018

me too!!!

Copy link
Member

@mjackson mjackson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for submitting this PR!

CHANGES.md Outdated Show resolved Hide resolved
modules/LocationUtils.js Outdated Show resolved Hide resolved
@mix3d
Copy link

mix3d commented Jun 29, 2018

@dabit1 any updates? Should someone else fix the suggested changes and recommit?

@undrafted
Copy link

Please can we have this PR updated @dabit1

@infalse
Copy link

infalse commented Aug 14, 2018

Hello. Struggled with the same problem of react-router and react-router-dom Link's being PUSHing history even if i was on the same /route already. What worked for me was:

import { withRouter } from 'react-router' 
import { NavLink } from 'react-router-dom' 
import { default as BSNavLink } from 'reactstrap/lib/NavLink' 

export const StyledLink = styled(BSNavLink)...

const NavLinkLogged = ({ to, history, staticContext, ...rest }) => {
  const shouldUseReplace = history.location.pathname === to
  return (
    <StyledLink to={to} tag={NavLink} replace={shouldUseReplace} {...rest} />
  )
}

export default withRouter(NavLinkLogged)

replace attribute becomes true when current location matches to path. So first time you click will be PUSH, and the others REPLACE.

Hope is helps

@undrafted
Copy link

undrafted commented Aug 15, 2018

I solved this problem by checking 'match' instead and rendering an empty a tag vs NavLink accordingly:

{match && match.isExact
            ? <a className={clsNavLink}>{content}</a>
            : <NavLink to={url} className={clsNavLink}>
              {content}
            </NavLink>}

@rowrowrowrow
Copy link

Hey, just wanted to say that this fix is important for me. I run into this issue when trying to render a component via a route when that component has a dynamically generated state. Thanks!!!

@dabit1
Copy link
Author

dabit1 commented Oct 19, 2018

Hi @mjackson! I did some changes in order to have it ready to be merged. A lot of people is requesting it so if you agree I think it's a good improvment.

modules/__tests__/LocationUtils-test.js Outdated Show resolved Hide resolved
modules/__tests__/LocationUtils-test.js Outdated Show resolved Hide resolved
@mjackson
Copy link
Member

mjackson commented Nov 1, 2018

Hey @dabit1, sorry for the delay here. Are you using React Router? If so, would you be open to fixing it there instead of here?

This library is designed to be a minimal abstraction over the browser's built-in history API. Someday we hopefully won't need it anymore. That's the only reason I'm hesitant to add more API that the browser has no equivalent for.

Having said that, I'm ready to move on this now. This bug is super annoying and has been here way too long. So whether we fix it here or in the router, it'll be fixed in our next release.

@mjackson mjackson added this to the 4.8 milestone Nov 1, 2018
@dabit1
Copy link
Author

dabit1 commented Nov 3, 2018

Hi @mjackson ! Nice to meet you finally hehe. So yes, I'm using React Router. But the thing is that in some cases I prefer or I have to use the method push of history instead of the Link component from React Router. For example, if you follow the Container pattern in React every callback of every presentational component is controlled by a container so presentational components can't decide what they can do after clicking them. For that reason I need to use the method push from the container and this is the reason Link can't be used.

Anyway I understand your point since this package is not only to be used for React. Maybe makes more sense if react-router-dom has a method link apart of Link component since only with Link component we don't do everything we need.

@guidobouman
Copy link
Contributor

guidobouman commented Nov 6, 2018

@mjackson I'm not sure that implementing this in ReactRouter only is a great idea. We've discussed this before in #470 (comment)

@tomasklim
Copy link

@mjackson hello, when this feature will be ready to use?

@siddhant91
Copy link

@mjackson Hello , is there any update on when will this feature be published and ready to be used ?

@cocacrave
Copy link

ETA please.

@vladshcherbin
Copy link

@cocacrave close to never since this PR is more than 1 year old. The long-standing issue will be 2 years soon. 👏

@mjackson mjackson closed this Mar 26, 2019
@cocacrave
Copy link

Anybody have a workaround you recommend and use?

@tomasklim
Copy link

tomasklim commented Mar 26, 2019

Somewhere I found this and it works fine:

let lastLocation = null;

history.listen(location => {
  lastLocation = location
});
const prevHistoryPush = history.push;

history.push = (pathname, state = {}) => {
  if (lastLocation === null || pathname !== lastLocation.pathname + lastLocation.search + lastLocation.hash ||
    JSON.stringify(state) !== JSON.stringify(lastLocation.state)) {
    prevHistoryPush(pathname, state);
  }
};

@cocacrave
Copy link

@tomasklim Thanks!

@guidobouman
Copy link
Contributor

Hey @mjackson,

If you don't have the time or drive to maintain this package, that's fine.

But might I suggest to let a team of contributors (that are willing to make sure this project stays alive) take care of it then? You could still be the owner, but others could help you out and implement the vision for the project. To shorten the time needed to get a PR merged, or get answers on issues.

@fabe
Copy link

fabe commented Mar 27, 2019

Here's another workaround I used a while ago to get around this (in my case, a page transition was called when navigating to the same page). Warning, it's hacky:

const getUserConfirmation = (location, callback) => {
  const [newUrl, action] = location.split('|');

  // Check if user wants to navigate to the same page.
  // If so, we don't want to trigger the page transitions.
  // We have to check the `action`, because the pathnames
  // are the same when going back in history 🤷‍
  const currentUrl = `${window.location.pathname}${window.location.search}`;
  if (newUrl === currentUrl && action === 'PUSH') {
    callback(false);
    return;
  }

  callback(true);
};

// ...

let history = createHistory({ getUserConfirmation });

// `block` must return a string to conform.
// We send both the pathname and action to `getUserConfirmation`.
history.block((location, action) => {
  return `${location.pathname}${location.search}|${action}`;
});

@vladshcherbin
Copy link

@guidobouman there is now a roadmap in issues for v4 and 5. However, this issue is not in the list, so I guess we are left with dirty hacks forever. 🤷‍♂️

@vladshcherbin
Copy link

For all watchers, there is another try now - #689 (comment) 🚀

@dabit1
Copy link
Author

dabit1 commented Mar 30, 2019

It's little bit annoying that I worked on it and then it's closed with no clear reason.

@lock lock bot locked as resolved and limited conversation to collaborators May 30, 2019
@mjackson
Copy link
Member

Hey everyone, I just realized that I never commented on this issue when I closed it. Sorry about that!

For anyone who is wondering, I left an explanation in another issue about why I decided to leave this functionality out of the history library.

It basically boils down to this: history.push is just a substitute for window.history.pushState. It shouldn't try to be "smart" about doing e.g. a push vs. replace. If it did, we would lose something that the pushState API actually gives us, namely the ability to push the same URL onto the stack twice. There are valid use cases for doing so, and overloading the meaning of "push" in this library would just cause more issues and confusion.

However, the original concern is totally legit. This is why React Router's <Link> component is smart about this in v6 (currently in beta). If you take a look at the source, you'll notice that a <Link> uses a replace instead of a push when the URL is the same. I think this is the right place for this logic.

This way, history + React Router have parity with the DOM. Our history.push works exactly like window.history.pushState and React Router's <Link> works like a normal <a> element.

Again, my apologies for not following up here earlier! I definitely should have.

@remix-run remix-run deleted a comment from vladshcherbin Aug 28, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet