Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Router redirect hash link

I have created a custom button component for my website's navbar. When the user clicks on a button, the component returns a Redirect, which takes the user to the page they selected.

export default class Button extends Component {
    constructor(props){
        super(props);
        this.state = {redirect:false};
        this._handleClick = this._handleClick.bind(this);
    }

    _handleClick(e) {
        e.stopPropagation();
        this.setState({redirect: true});
    }

    componentDidUpdate() {
        if (this.state.redirect){
            this.setState({redirect:false});
            this.props.onRedirect();
        }
    }

    render() {
        if (this.state.redirect){
            return <Redirect push to={this.props.dest}/>;
        }
        else {
            return (
                <li className="button" onClick={this._handleClick}>
                    <h5>{this.props.text}</h5>
                </li>
            );
        }
    }
}

Now, I'd like to add buttons that correspond to different sections of the same page. The simplest way I know of is to use hash links. One example of an address the button would redirect to is:

/home#description

However, React Router does not support doing this out of the box. I looked through a number of packages which add this functionality, such as react-router-hash-link and react-scrollchor. None of these however work with redirects, instead relying on Link or on custom components.

How do I go about adding this functionality to the buttons?

like image 756
stelioslogothetis Avatar asked Aug 24 '17 14:08

stelioslogothetis


2 Answers

you could update window.location.href since it won't trigger a page refresh.

e.g.

window.location.href = '#your-anchor-tag';
like image 191
pureth Avatar answered Oct 07 '22 14:10

pureth


One solution that I can think of is to use HOCs and hooks. The end result:

  • You'll get your app to scroll to the specified location...
  • without really needing to create custom buttons/links and...
  • without making much changes to your existing screens (Eg: HomeScreen)
  • Bonus: Users can copy, share & use URLs that will automatically scroll to the intended section


With assumption that the code below are pseudocode (they are based on my knowledge and not tested) and assuming there's a HomeScreen component, I would attempt adding <Route/>s to the <Switch/> inside the <Router/>.

<Switch>
  <Route to='/home/:section' component={HomeScreen} />
  <Route to='/home' component={HomeScreen} />
</Switch>


Then:

function withScrollToTarget(WrappedComponent) {
  class WithScroll extends React.Component {
    componentDidMount() {
      const { match: { params: { section } } } = this.props
      // Remember we had 2 <Route/>s, so if `section` is provided...
      if (section) {
        const scrollToTarget = document.getElementById(section)
        // And just in case the item was removed or there was an ID mismatch
        if (scrollToTarget) { scrollToTarget.scrollIntoView() }
      }
    }
    render() { return <WrappedComponent {...this.props} /> }
  }
  return WithScroll
}

function useScrollToTarget(section) {
  useEffect(() => {
    if (section) {
      const scrollToTarget = document.getElementById(section)
      if (scrollToTarget) { scrollToTarget.scrollIntoView() }
    }
  }, [section])
}


Usage:

<nav>
  <Link to='/home'>{'Home'}</Link>
  <Link to='/home/description'>{'Description'}</Link>
</nav>


class HomeScreen extends React.Component { /* ... */ }
export default withScrollToTarget(HomeScreen)
// or
function HomeScreen() {
  const { params: { section } } = useMatch() // from react-router-dom
  useScrollTotarget(section)
  return (
    <div>
      <h1 id='introduction'>Introduction</h1>
      <h1 id='description'>Description</h1>
    </div>
  )
}


TLDR:

  • The route for '/home/:section' must be on top of '/home'. If the opposite, every time when <Switch/> compares the current URL against to, it will evaluate to true upon reaching '/home' and never reach '/home/:section'
  • scrollIntoView() is a legit function
  • If this works for you, you should look up on how to forward refs and hoisting statics in HOCs too
like image 2
GlyphCat Avatar answered Oct 07 '22 12:10

GlyphCat