Friday, June 28, 2019

How To Scroll To The Top of The Page in React and Typescript?

Issue:
When navigating into another page, its position will remain same like the previous page but you want to scroll to the top of new routed/rendered page.

Solution
I usually put an empty div on top of that page, and gave it a ref, so that, later I can use the scrollIntoView() method to scroll the page into that div.

In TypeScript, you'll have more extra steps to do so, seems to be annoying, but it's actually very helpful because you can take advantages of the type system. First, define a ref like this:
class YourComponent extends React.Component {
  private scrollAnchorRef: any;

  //or private scrollAnchorRef: RefObject<HTMLElement>

  constructor(props) {
    super(props);
    this.scrollAnchorRef = React.createRef(); // Create a ref object 
  }

  render() {
    <div ref={this.scrollAnchorRef}>
      ...
    </div>
  }
}

I have used any type instead of a generic HTMLElement type (recommended) when defining a ref, because it will help the compiler narrow down the type so you'll have better suggestion in the next step.

Now, when you finish rendering/loading page and want to scroll to the top of element, you can do:
  this.scrollAnchorRef.current.scrollIntoView({ block: 'start', behavior: 'smooth' })
And you'll get an error message says: TS2531: Object is possibly 'null'

The reason is, RefObject.current has the type of T | null, because React will actually set an element's ref into null when it re-render, and if you're trying to scroll during re-render, you'll run into problems.

The solution is simple, add an existent check:
 const anchor = this.scrollAnchorRef;
 if (anchor) {
   anchor.current.scrollIntoView({ block: 'start', behavior: 'smooth' }); 
}
Here is the complete solution.

class YourComponent extends React.Component {
  private scrollAnchorRef: any;

  constructor(props) {
    super(props);
    this.scrollAnchorRef = React.createRef(); // Create a ref object 
  }

  componentDidUpdate(prevProps) {
    if (this.props.location !== prevProps.location) {
      const anchor = this.scrollAnchorRef;
     if (anchor) {
        anchor.current.scrollIntoView({ block: 'start', behavior: 'smooth' }); 
     }
    }
  }

  render() {
   <div ref={this.scrollAnchorRef}>
      ...
   </div>
  }
}

This is how type saved your butt when you work with React and Typescript.

Reference: React, TypeScript and scrollInToView