Categories
React Tips

React Tips — Scroll, Navigate, and Keeping Focus

Spread the love

React is a popular library for creating web apps and mobile apps.

In this article, we’ll look at some tips for writing better React apps.

React Router Scroll to Top on Every Transition with Hooks

We can create a wrapper component to scroll a route component to the top whenever we navigate.

For instance, we can write:

import React, { useEffect, Fragment } from 'react';
import { withRouter } from 'react-router-dom';

function ScrollToTop({ history, children }) {
  useEffect(() => {
    const unlisten = history.listen(() => {
      window.scrollTo(0, 0);
    });
    return () => {
      unlisten();
    }
  }, []);

  return <Fragment>{children}</Fragment>;
}

export default withRouter(ScrollToTop);

We call history.listen to listen for navigation.

In the callback for it, we call window.scrollTo(0, 0) to scroll to the top.

It returns a function to clear the listener, so we call that in the function that we return in the useEffect callback.

We pass in an empty array to useEffect to attach the listener only once when the ScrollToTop component is mounted.

Then we can use it by writing;

<Router>
  <ScrollToTop>
    <Switch>
       <Route path="/" exact component={Home} />
    </Switch>
  </ScrollToTop>
</Router>

We wrap ScrollToTop around all our routes so that scrolling to top is done when any route component loads.

React Router and withRouter

withRouter is a component that we can call to let us get the location , history , and match objects as props in our component.

For instance, we can write:

import React from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router";

class App extends React.Component {
  static propTypes = {
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired
  };

  render() {
    const { match, location, history } = this.props;
    return <div>{location.pathname}</div>;
  }
}

const ShowTheLocationWithRouter = withRouter(App);

match is an object that has information about a router’s path.

It has the params property to get the segments of the path.

isExact is true if the path matches the URL.

path is a string with the pattern that’s used to match.

url is a string that has the matched portion of the URL.

location is an object that has the pathname with the pathname.

search is the query string portion of the URL.

hash has the part of the URL after the # sign.

history is the native history object.

It has the length with the number of entries in the history stack.

The location property has the parts of the URL returned as an object.

It’s the same as the location object itself.

push is a method to let us navigate to a new path.

replace lets us navigate to a new path by overwriting history.

go navigate to a history entry by the number of steps.

goBack moves to the previous history.

goFoward lets us go to the path in the next history entry.

React Components from Array of Objects

We can render an array of objects into a list of component by using the map method.

For instance, we can write:

const Items = ({ items }) => (
  <>
    {items.map(item => (
      <div key={items.id}>{items.name}</div>
    ))}
  </>
);

All we do is call map with a callback to return the entries derived from the items entry.

key should have a unique ID so that React can distinguish each entry even if they’ve moved.

Fix Input Losing Focus When Rerendering Problem

This may happen if we create our own input component and we created it within the render method.

This will cause a new instance of our custom input component to be created every time the input is rendered.

Therefore, we should move it out of render .

We can keep the input focused by writing:

import React from 'react';
import styled from 'styled-components';

const Container = styled.div``;
const Input = styled.input``;

class App extends React.Component {
  constructor(){
    this.state = {
      name: ''
    };
    this.updateName = this.updateName.bind(this);
  }

  updateName(e){
    e.preventDefault();
    this.setState({ name: e.target.value });
  }

  render() {
    return (
      <Container>
        <Input
          type="text"
          onChange={this.updateName}
          value={this.state.name}
        />
      </Container>
    )
  }
}

We move the Input and Container outside the App component to create the components outside rather than on the fly inside.

This means that the same instance will always be used.

And the input will stay focused when App rerenders.

Conclusion

We shouldn’t create any components on the fly without our component so that the same instance is always used.

We can create our own component to scroll a component to the top when we navigate to a different route.

withRouter injects a few objects to our component.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *