Help
Support Us

Side Effects

Side effects are bits of code that run when changes happen in the Virtual DOM tree. They don't follow the standard approach of accepting props and returning a new Virtual DOM tree, and often reach out of the tree to mutate state or invoke imperative code, like calling into DOM APIs. Side effects are also often used as a way to trigger data fetching.

Effects: side effects in function components

We've already seen one example of side effects in action in a previous chapter, when learning about refs and the useRef() hook. Once our ref was populated with a current property pointing to a DOM element, we needed a way to "trigger" code that would then interact with that element.

In order to trigger code after rendering, we used a useEffect() hook, which is the most common way to create a side effect from a function component:

import { useRef, useEffect } from 'preact/hooks';

export default function App() {
  const input = useRef()

  // the callback here will run after <App> is rendered:
  useEffect(() => {
    // access the associated DOM element:
    input.current.focus()
  }, [])

  return <input ref={input} />
}

Notice the empty array being passed as a second argument to useEffect(). Effect callbacks run when any value in that "dependencies" array changes from one render to the next. For example, the first time a component is rendered, all effect callbacks run because there are no previous "dependencies" array values to compare to.

We can add values to the "dependencies" array to trigger an effect callback based on conditions, rather than just when a component is first rendered. This is typically used to run code in response to data changes, or when a component is removed from the page ("unmounted").

Let's see an example:

import { useEffect, useState } from 'preact/hooks';

export default function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('<App> was just rendered for the first time')
  }, [])

  useEffect(() => {
    console.log('count value was changed to: ', count)
  }, [count])
  //  ^ run this any time `count` changes, and on the first render

  return <button onClick={() => setCount(count+1)}>{count}</button>
}

Lifecycle methods: class component side effects

Class components can also define side effects, by implementing any of the available lifecycle methods provided by Preact. Here are a few of the most commonly used lifecycle methods:

Lifecycle method When it runs:
componentWillMount just before a component is first rendered
componentDidMount after a component is first rendered
componentWillReceiveProps before a component is re-rendered
componentDidUpdate after a component is re-rendered

One of the most common examples of side effect usage in a class component is to fetch data when a component is first rendered, then store that data in state. The following example shows a component that requests user information from a JSON API after the first time it gets rendered, then shows that information.

import { Component } from 'preact';

export default class App extends Component {
  // this gets called after the component is first rendered:
  componentDidMount() {
    // get JSON user info, store in `state.user`:
    fetch('/api/user')
      .then(response => response.json())
      .then(user => {
        this.setState({ user })
      })
  }

  render(props, state) {
    const { user } = state;

    // if we haven't received data yet, show a loading indicator:
    if (!user) return <div>Loading...</div>

    // we have data! show the username we got back from the API:
    return (
      <div>
        <h2>Hello, {user.username}!</h2>
      </div>
    )
  }
}

Try it!

We'll keep this exercise simple: change the code sample on the right to log every time count changes, rather than only when <App> is first rendered.

Loading...