React router - Update URL hash without re-rendering page

Using react-router I'm looking for a way to update the page URL / hash, without the router re-rendering the whole page.

I am developing a full page carousel, and would like each slide to have it's own URL (allowing the user to refresh the page and return to the correct slide). The carousel will later have swipe similar to this demo, which means the next slide is pre-rendered.

A stripped down version of my carousel is available here.

The current slide change looks like this:

onClickLeft: function() {
  this.setState({
    selected: this.state.selected - 1
  });
}

This works fine, with no URL updates. What I really want is:

mixin: [Navigation],
onClickLeft: function() {
  this.transitionTo('carousel-slide', {num: this.state.selected + 1});
}

This would set the prop of the current slide, allowing the carousel to animate. However using this method now causes the page to re-render and no animation is displayed.

I have seen the ReactCSSTransitionGroup used for route transitions, however this seems geared toward rendering a new page and transitioning out the old one.

If there's already a way to achieve what I'm looking for, and I've missed it, could someone point me in the right direction?

Upvotes: 35

Views: 55962

Answers (4)

Marten
Marten

Reputation: 804

React Router v6

JavaScript

If you really want to do this with React Router, you can look at some workarounds in this issue or look at the HTML solution below. However, I would just recommend doing it using the window object.

If you want it plain and simple, set the hash like this: window.location.hash = '#newHash'.

If you don't want to push something to the history stack, use window.location.replace, like in the following example: window.location.replace('#newHash').

HTML (<Link>)

Use an anchor tag, if you only want to link to a hash, and you don't need any React Router features.

If you just want to create a <Link to=""> element, you can set reloadDocument to true, like in the following example: <Link reloadDocument to="#test"></Link>. This skips client side routing and lets the browser handle the transition normally as it were an anchor tag. Read more about it here. Make sure React Router renders the to="[this link]" link correctly! If you want, like me, that the hash will not get pushed to the user's history, you also need to set the replace attribute on the link element to true. However, this solution does not work, if you have search parameters.

Upvotes: 4

Matthew Barbara
Matthew Barbara

Reputation: 3962

For React router v4 you need to use history and pass it as prop to the Router. Router will then pass history object to your component as a prop and you can use it like so:

// In your component

this.props.history.push("#testing")

History file

// history.js 
import createHistory from "history/createBrowserHistory";

export default createHistory();

Then, wherever you are declaring your router, pass the history prop

import history from "./history";
import { Router } from "react-router-dom";

    <Router history={history}>
      // ... Your component
    </Router>

Upvotes: 6

Mr.Wang from Next Door
Mr.Wang from Next Door

Reputation: 14790

It is a pain to work with react-router because it involves breaking changes in every major and minor release

React Router v2.0 rc5

import { hashHistory } from 'react-router'
this.props.location.query.t = key;
hashHistory.replace(this.props.location);

Upvotes: 11

Brigand
Brigand

Reputation: 86220

1.0

react-router no longer sets a key on your routes. If you do need to set a key from a route handler, put it on a surrounding element.

return (
  <div key={this.props.params.bookId}>
    {this.props.children}
  </div>
);

0.13

It's now <ReactRouter.RouteHandler key="anything" />, but this is also no longer really needed due to changes in react-router. See the changelog for more details.

0.12

Currently, react-router sets a key on your handler based on the current route. When react does its diff, and notices a different key, it throws out the entire subtree both in virtual and real dom, and rerenders.

To prevent this, you can override react-router's key when using activeRouteHandler()

this.props.activeRouteHandler({key: "anything"})

Upvotes: 12

Related Questions