Skip to content

Przemysław Konieczniak - Dev Notes

Pure Functions

Functional Programming, JavaScript, React

Introduction

Pure functions are functions which for the same input produces always the same output. Additionally, pure functions do not cause side effects.

The example of impure functions are the built-in methods like shift, pop, sort, splice, which operates on arrays and mutate them.

impure
1const arr = [1, 2, 3, 4, 5]
2arr.shift() // 1
3arr.shift() // 2
4arr.shift() // 3
5
6arr // [4, 5]
pure
1const arr = [1, 2, 3, 4, 5]
2const getElement = array => index => array[index]
3getElement(arr)(0) // 1
4getElement(arr)(0) // 1
5getElement(arr)(0) // 1
6
7arr // [1, 2, 3, 4, 5]

Pure functions must depend only on their passed arguments or internal state.

When the function body contains the values from the external scope, we can't be sure that these value will never change during the program execution and it makes the function impure.

Side Effects

Side effect is any change in the system state, that is observable outside the called function other than its return value.

Examples of side-effects:

  • HTTP request
  • Logging to the console
  • Manipulating the file system
  • Changing the global state or the state that is beyond of the scope of executing function
  • Updating DOM
  • Triggering events
  • Calling inside the pure function any impure function

Advantages of Pure Functions

  • Memoization

    Because pure functions always return the same value for the same input we can use the memoization technique. This technique prevents invoking the function, which has been already executed. Instead of calling the function, the calculated result is returned from the cache.

  • Easy testing

    Pure functions depend on the provided arguments and on the internal state, due to this fact writing the tests becomes easier. We don't have to mock dependencies because we explicitly passing values to the function. We expect that for the provided input we'll receive particular output.

  • Readability

    Pure functions make code easier to understand because they contain everything that they need. There is no need to scan other files or look for the global variables. Everything that is needed to understand the function is inside its body.

  • Portability

    Because pure functions do not depend on any external state or specific business logic it can be easily transferred to the other projects. It is a huge possibility that some utilities that you use at this moment will be needed in the next project.

Bonus - useEffect

Because React hooks derive a lot from the FP concepts let's look on the useEffect hook to see what can happen when we are using the variables from the "outside" world.

Before we start, a quick reminder of what useEffect hook does do.

useEffect performs side effects in a component. It can be fetching a data from REST API or manipulating the DOM. This hook is a combination of componentDidMount and componentDidUpdate methods. By default, this effect runs after every completed render, but it is possible to run this hook, only when certain values have changed.

When we would like to run this hook only once, we usually passing an empty array. It means that the hook does not depend on any values and should be run only once.

exhaustive-deeps
1import React, { useEffect } from 'react'
2export const Container = ({ userId }) => {
3 useEffect(() => {
4 loadUser(userId)
5 .then(() => {/**/})
6 .catch(() => {/**/})
7 }, [])
8}

At first glance, the above code looks good, but inside of the effect, we are using the variable which is not provided in the dependencies list. If we have set-up a linter we can see react-hooks/exhaustive-deps error.

Because we are talking about the pure functions, the useEffect hooks needs an explicit list of arguments from which depends firing the hook. Everything that is used in the useEffect must be passed in the array. This approach prevents unexpected behavior like do not firing the effect when the userId has changed.

1import React, { useEffect } from 'react'
2export const Container = ({ userId }) => {
3 useEffect(() => {
4 loadUser(userId)
5 .then(() => {/**/})
6 .catch(() => {/**/})
7 }, [userId])
8}

Resources

© 2020 by Przemysław Konieczniak - Dev Notes. All rights reserved.