Using "window" the React Way with react-fns

William Le

Have you had the funny feeling that window.addEventListener didn’t really belong in your React component? That’s your React intuition telling you that you’re writing imperative-style code! Learn about a new library, react-fns, that wraps imperative HTML5 APIs with a declarative interface.

This is the conventional way of listening to window scrolling events:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      scrollY: 0
    };
  }
  componentDidMount() {
    window.addEventListener('scroll', this.handleScroll);
  }
  handleScroll = () => {
    this.setState({scrollY: window.scrollY});
  }
  render() {
    return <Content scrollY={this.state.scrollY}/>
  }
  componentDidUnmount() {
    window.removeEventListener('scroll', this.handleScroll);
  }
}

Even though we’ve gotten comfortable with doing this… there are some downsides. The component is littered with logic to handle this scroll feature. In a React component, this “bloat” is a telltale sign of an imperative style of thinking:

  • “Let’s listen to scroll events”
  • “Ok. We need to do something with the scroll data…”
  • “Unlisten when the component unmounts”

The component becomes brittle and resistant to refactoring. As a quick mental exercise, imagine how you would refactor the scroll listener into its parent component. You’d need to move almost everything to the parent… yeesh!

react-fns

react-fns hides the details and gives you a programming interface to HTML5 that’s more declarative:

import { Scroll } from 'react-fns';

class App extends React.Component {
  render() {
    return (
      <Scroll
        render={({ x, y }) => (
          <Content scrollY={y}/>
        )}
      />
    );
  }
}

You’re just telling it how to behave with data… that’s a declarative style of programming! This approach reduces “lines of code,” and it’s also really easy to know what’s happening from a quick glance. If you needed to refactor the scroll listener it wouldn’t be error prone, and a lot more straight-forward.

react-fns offers several popular HTML5 APIs. These are the ones I use a lot:

import { WindowSize, Scroll, GeoPosition, Network } from 'react-fns';

class App extends React.Component {
  render() {
    return (
      <div>

        <Scroll
          render={({ x, y }) => (
            <div>
              Scroll:
              {x}, {y}
            </div>
          )}
        />

        <WindowSize
          render={({ width, height }) => (
            <div>
              Window Dimensions:
              {width}, {height}
            </div>
          )}
        />

        <GeoPosition
          render={({ isLoading, coords, error }) => (
            <div>
              Longitude, Latitude:
              {coords && `${coords.longitude},${coords.latitude}`}
            </div>
          )}
        />

        <Network
          render={({ online, offlineAt }) => (
            <div>
              Network Status:
              {online ? 'Online!' : 'Offline'}
              {offlineAt && `Last connected ${offlineAt.toISOString()}`}
            </div>
          )}
        />

      </div>
    );
  }
}

See a demo of these components in action! 🌈

While react-fns doesn’t have exhaustive coverage of all the HTML5 APIs, they do have the most popular ones. To view their full list of React components, visit their docs page.

When using some of the popular HTML5 APIs, check out react-fns and see if it can help you write slimmer and more declarative React components.

👉 Check out a demo of several wrappers that react-fns offers

  Tweet It

🕵 Search Results

🔎 Searching...