Reputation: 8921
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?
Upvotes: 9
Views: 10612
Reputation: 526
I was trying to solve a similar but slightly different issue, I want to deprecate an old hash route in favor of a new one. The posts here helped me arrive to my eventual solution:
<Route
exact
path={'/thing/:id'}
render={({
match: {
params: { id },
},
}) => (
<Redirect
push
to={`/newThing/${id}`}
/>
)}
/>
Upvotes: 0
Reputation: 1606
Who said React Router doesn't support this out of the box! You don't need those packages. You can redirect a hash i'll give you an example using the React-Router Route.
<Route
exact
path="/signup"
render={props => {
if (props.location.hash === "#foo")
return <Redirect push to="signup#bar"
return <Signup />
}}
/>
Now your version may not have supported this now that I think about it, but let me know if this helps :) Happy coding!
Upvotes: 2
Reputation: 436
I was facing the same issue, I have created HOC to handle hash redirection, you can follow the below steps to achieve a hash redirection
fileName : hashComponent
import React, { useEffect } from 'react';
export default function hashComponent(WrappedComponent) {
return function () {
const { pathname, hash }=window.location;
useEffect(() => {
if(hash)
window.location.href=`${pathname}${hash}`;
}, [hash])
return <WrappedComponent />
}
}
import your HOC in the component to which you want to handle hash URL
Then add below line of code while exporting your component
export default hashComponent(YourComponentName)
Upvotes: -1
Reputation: 180
One solution that I can think of is to use HOCs and hooks. The end result:
HomeScreen
)
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:
'/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 functionUpvotes: 2
Reputation: 181
I think you should use the react-router-dom.
yarn add react-router-dom
Now update Custom Button Component like this
import React from 'react';
import { withRouter } from "react-router-dom";
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();
this.props.history.push('new uri');
}
}
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>
);
}
}
}
export default withRouter(Button);
Upvotes: 0
Reputation: 421
React-hash-link should work for your redirect use case.
You can add <HashLinkObserver />
to your component tree and it will listen for hash links and scroll accordingly rather than relying on Link
or custom components.
Upvotes: 0
Reputation: 838
you could update window.location.href
since it won't trigger a page refresh.
e.g.
window.location.href = '#your-anchor-tag';
Upvotes: 2