Understanding the concepts of functional programming is a valuable skill for React developers. This is an important topic that most React beginners often ignore, causing them problems in understanding how React makes some decisions.
In this article, we’ll introduce the concept of functional programming and how React can adopt it to write applications that are easier to test and maintain.
To follow this tutorial, make sure you have a basic understanding of React.
A quick overview of functional programming
Every program or application we write follows an approach or writing style, known as a paradigm. Thus, functional programming is a declarative programming paradigm in which programs are composed of pure functions.
Let’s pay attention to the words “composition” and “pure” because they form the building blocks of functional programming and we’ll discuss them in the following sections.
Function in mathematics
To better understand the concepts of functional programming, let’s take a quick look at functions that are common in mathematics. For example, we have a given function.
y = f(x)
Copy the code
In this function, the output, y, evaluates only the input, x. That means that every time we call this function with the same input x, we always get the same output y.
This function does not affect anything other than itself, nor does it modify the incoming input. Therefore, it is called a deterministic or pure function.
Let’s look at an example.
y = f(x) = 4x // if x = 2, y = 4(2) = 8
Copy the code
As seen above, for each input, x = 2, the output y will always be 8. Functions like this are always easier to understand and reason about because we know exactly what we expect.
Let’s go one step further and write a more complicated function like this.
z = c(f(x))
Copy the code
In this case, we have two functions, c and f, which are combined to form a more complex function. In mathematics, we say that C is a function of f(x), which means we have to evaluate f(x) separately first, like this.
y = f(x)
Copy the code
And then, we pass the result as a parameter to C, like this.
z = c(y)
Copy the code
This functional concept is called function composition. It encourages reusability and maintainability of code.
With this mathematical concept, understanding functional concepts in computer science programming is a piece of cake.
Functional programming in React
Next, we’ll explain how functional programming concepts are applied in React. We’ll also see examples in JavaScript, since it’s the underlying language for React.
Let’s take a look at the JavaScript code below.
const arr = [2, 4, 6];
function addElement(arr, ele) {
arr.push(ele);
}
addElement(arr, 8);
console.log("original data", arr); // Expected Output: [2, 4, 6, 8]
Copy the code
Here, we define a function called addElement that adds an element to an array. This code works as shown in the output, so you can try it out for yourself on CodeSandbox.
This code looks similar to the function concept in the math explained earlier. That is, a function operates only on input parameters to create an output.
But a closer look at this code reveals that it violates a pure function concept, namely that a function should not affect anything outside of it, nor should it modify the parameters passed in.
A function that does this is an impure function and has side effects, such as manipulating input parameters and, in this case, global ARR arrays.
In the code, the global arr array is mutated, that is, the function changes the global variable from the original [2,4,6] to [2,4,6,8].
Now, imagine that we want to reuse this global variable to form another function. If we continue to do this, we will get an unexpected output that may cause an error in our program.
This brings us to the first tenet of functional programming: pure functions.
Using pure functions
In functional programming, we write pure functions, that is, functions that return output only on input values, never affecting anything beyond them. This helps prevent errors in our code.
Applying this functional concept to the code above, we get the following results.
const arr = [2, 4, 6];
function addElement(arr, ele) {
return [...arr, ele];
}
console.log("modified data", addElement(arr, 8)); // Expected Output: [2, 4, 6, 8]
console.log("original data", arr); // Expected Output: [2, 4, 6]
Copy the code
The above function is pure, it evaluates the output only on the input parameters and never changes the global ARR, as we can see from the results.
You can try it out for yourself at CodeSandbox.
Note that this code also uses the concept of immutability to make the function pure (we’ll discuss this later).
This type of function is predictable and easier to test because we always get the expected output.
How does React implement the concept of pure functions
The simplest form of a React application component looks like this.
const Counter = ({ count }) => {
return <h3>{`Count: ${count}`}</h3>;
};
Copy the code
This is similar to pure functions in JavaScript, where a function takes a parameter (in this case, a count item) and uses that item to render the output.
However, the React data flow is one-way, from parent to child. When state data is passed to a child component, it becomes an immutable item that cannot be modified by the receiving component.
Therefore, this type of component always renders the same JSX given the same item. And, because of this, we can reuse this component in any part of the page without worrying about uncertainty. This type of component is a purely functional component.
Improve application performance
React leverages the concept of pure functionality to improve application performance. Due to the nature of React, whenever a component’s state changes, React rerenders the component and its children, even if the state change does not directly affect the children.
In this case React allows us to use React. Memo to prevent unnecessary re-rendering if it receives items that never change.
By memorizing the pure function above, we re-render it only when the count item changes.
const CounterComponent = React.memo(function Counter({ count }) {
return <h3>{`Count: ${count}`}</h3>;
});
Copy the code
Pure functional concepts in status updates
React also implements the functional concept when updating state variables, especially when a value is based on a previous value, as in the case of a counter or check box.
Look at the code below.
import { useState } from "react"; const App = () => { const [count, setCount] = useState(0); const handleClick = () => setCount(count + 1); return ( // ... ) ; }; const Counter = ({ count }) => { // ... }; export default App;Copy the code
Here, for brevity, we removed some of the code, but extended our previous Counter component to display a button to add a count.
This is familiar code for newcomers to React. While this code works, we can add improvements by following the concepts of functional programming.
Let’s focus on this part of the code.
const handleClick = () => setCount(count + 1);
Copy the code
In the setCount updater function, we use a count variable that is not passed as an argument. As we’ve seen, this goes against the concept of functional programming.
One improvement that React provides is to pass a callback to the updater function. In this callback, we can access the previous version of the state, and from there, we can update the state value.
const handleClick = () => setCount((prev) => prev + 1);
Copy the code
As we saw in the setCount callback, the output is evaluated only on the prev input parameter. That’s where the pure functional concept comes in.
Avoid variation data (immutability)
When a function mutates or changes a global variable, it causes an error in our program.
In functional programming, we treat mutable data structures such as arrays and objects as immutable data. This means that we never modify them, but instead make a copy when passed to the function so that the function can calculate its output from this copy.
Let’s revisit the following code.
const arr = [2, 4, 6];
function addElement(arr, ele) {
return [...arr, ele];
}
console.log("modified data", addElement(arr, 8)); // Expected Output: [2, 4, 6, 8]
console.log("original data", arr); // Expected Output: [2, 4, 6]
Copy the code
We’ve looked at this code before, but this time we’ll shift our focus to the immutable function aspect.
Here, we have a function that evaluates the output using only a copy of the global variable. We use the ES6 propagation operator (…) Copy the existing data into a new array, and then add the new elements. In this way, we preserve the immutability of the original array input data, as you can see in the result.
React How does React handle volatile states
Because React is a reactive library, it must “React” to state changes to keep the DOM up to date. Obviously, the status values must also be updated.
In React, we don’t change the state directly. Instead, we use the setState() method in the class component or the updater function in the function component to update the state.
Take a look at our previous code excerpt.
const handleClick = () => setCount((prev) => prev + 1);
Copy the code
Here, we use the updater function, setCount, to update the count. When dealing with immutable data such as numbers and strings, we must only pass the updated value to the updater function, or call the callback function when the next state depends on the previous state.
Let’s look at another example of updating a string value.
import { useState } from "react"; const App = () => { const [person, setPerson] = useState(""); const handleChange = (e) => { setPerson(e.target.value); }; return ( // ... ) ; }; export default App;Copy the code
Here, we’ve removed some more code for brevity.
The above code updates the text field of a form, which involves handling immutable string data. So, we must update the input field by passing the current input value to the updater function.
However, whenever we pass mutable data like arrays and objects, we must make a copy of the state data and calculate the output against that copy. Note that we must not modify the raw state data.
In the code below, handleChange, updates the status variable on every key in the form.
import { useState } from "react"; const App = () => { const [person, setPerson] = useState({ fName: "", lName: "" }); const handleChange = (e) => { setPerson({ ... person, [e.target.name]: e.target.value }); }; return ( // ... ) ; }; export default App;Copy the code
As you can see from the code, we are dealing with a mutable object, so we must treat the state as immutable. Again, we do this by making a copy of the state and updating the affected properties using the ES6 propagation operator.
setPerson({ ... person, [e.target.name]: e.target.value });Copy the code
Another improvement is to ensure that the updater function setPerson uses a state variable that is passed as an argument to the callback function.
const handleChange = (e) => { setPerson((person) => ({ ... person, [e.target.name]: e.target.value })); };Copy the code
Now, what happens if we don’t follow this functional concept and just mutate the state? Obviously, our application is going to have an error.
To see a clearer picture, visit the CodeSandbox again and comment out temporarily from the function… Person, like that.
setPerson((person) => ({ // ... person, [e.target.name]: e.target.value }));Copy the code
Now, by trying to write something in a form field, the text will overwrite each other. This is a mistake we want to prevent, and we can do this by treating the state as immutable data.
Avoid side effects
The purpose of functional programming code is pure. Pure components in React can take an item as an argument and calculate the output based on the input item.
Sometimes, however, the component can perform calculations that affect and modify some state outside its scope. These calculations are called side effects. Examples of these effects include data retrieval and manual manipulation of the DOM.
These are tasks that we often perform in our applications, so side effects are inevitable.
The following snippet is based on our previous Counter example.
const Counter = ({ count }) => {
document.title = `Number of click: ${count}`;
return <h3>{`Count: ${count}`}</h3>;
};
Copy the code
In the code, we update the title of the document to reflect the updated count. This is a side effect because we have modified DOM elements that do not belong to the component, making the component impure.
Performing side effects directly within the component body is not allowed to avoid inconsistencies in our application. Instead, we must isolate this effect from the rendering logic. The React provides us with a Hook called [useEffect] (https://blog.logrocket.com/guide-to-react-useeffect-hook/) to manage our side effects.
The following code implements this Hook.
const Counter = ({ count }) => {
useEffect(() => {
document.title = `Number of click: ${count}`;
}, [count]);
return <h3>{`Count: ${count}`}</h3>;
};
Copy the code
By moving the side effects on the React [useEffect] (HTTP: / / https://codesandbox.io/s/admiring-hoover-s52eg? File =/ SRC/app.js) Hook, which means we can easily test and maintain the rendering logic.
React composition
In functional programming, composition is the act of building a complex function by combining or concatenating several smaller functions.
If we recall from the beginning of this article, we said that for a given function, c and f, we can combine them into a more complex function, demonstrated like this.
z = c(f(x))
Copy the code
But for now, we’ll take a look at the composition concept in the context of React.
Similar to the functional pattern above, we can build a complex component in React by injecting other components with the Children item of React. This item also allows a component to render different amounts of content without needing to know in advance what the content is.
This gives us the flexibility to decide what is inside the component and customize the content to get the desired output.
Implement the concept of a good example of components including the Hero and [Sidebar] (https://blog.logrocket.com/create-sidebar-menu-react/).
Build a reusable oneHero
component
Suppose we want to create a Hero component with different content, we can reuse it anywhere in our application.
We can start writing components like this.
function Hero({ children }) {
return <section>{children}</section>;
}
Copy the code
The Children item used in this code allows us to inject content between the opening and closing tags of a component; In our case, a Hero component.
So, we can have something like this.
<Hero>
<Banner>
<h1>Home Page</h1>
<p>This is the home page description</p>
</Banner>
</Hero>
Copy the code
Now, everything between
is considered an item for its children and therefore appears between the section tags of the Hero component.
Similarly, the contents of the
JSX tag enter the Banner component as children Prop.
function Banner({ children }) {
return (
<div>
{children}
<button>Subscribe to newsletter</button>
</div>
);
}
Copy the code
The content between the
tags (that is, h1 and P) replaces children in JSX.
In this code, the Banner component only knows about the button element because we added it manually; It doesn’t know what will replace children’s items.
This makes the component reusable and flexible for customization because we can control the content as children. We can now decide not to render the banner title, H1, on another page of our application.
We have to do is put it out of content, in the [Banner] (https://codesandbox.io/s/angry-chebyshev-69hiw? File =/ SRC/app.js) tag.
By comparing the React composition with the mathematical definition, we can say that the output of the Banner component becomes the input of the Hero component. In other words, the Hero and Banner components form a complete component.
This is the combination in practice.
conclusion
I’m glad you’re here! In this article, we saw how React applies functional programming concepts to make some decisions through practical examples.
I hope you enjoyed reading this article. If you have questions or contributions, please share your thoughts in the comments section and I’ll be happy to host them. Finally, make an effort to share this guide online.
The post Fundamentalsof functional programming with Reactappeared first onLogRocket Blog.