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.