Functional Programming Techniques
As a language, JavaScript is conducive to functional programming. The notes below cover some of the more common functional programming techniques in JavaScript.
Hooking
⚠️ This section assumes familiary with the React library.
Hooking is a functional programming pattern for handling side-effects. In a broad sense, hooks are simply wrappers for generic callback functions. To understand why hooks exist, consider the following React component:
export const Greet = ({ greeting }) => {
return <div>{greeting}</div>;
};
to use this component, we simply write:
<Greet greeting="Hello" />
This is React's approach to creating web applications. The entire application is some state, and to change how the application looks or behaves, we modify its state. Hooks are patterns for performing these modifications.
UseState
Suppose we had the following component:
export const Counter = () => {
let counter = 0;
return <div>{counter}</div>;
};
Now we want a button with the following action: If we click the button, the
value increments. So, we modify our component by including a button. We
then assign an incrementing function to the button's onClick
attribute:
export const Counter = () => {
let counter = 0;
const increment = () => {
counter = counter + 1;
};
return (
<div>
{counter}
<button onClick={increment}>++</button>
</div>
);
};
If we click on the button above, nothing happens. The counter
variable,
however, is in fact changing. So why aren't we seeing the value change?
Because React doesn't know that the application's state has changed. The
counter
variable changed, but that variable is not actually hooked onto
the application's state. And because it isn't hooked on to the
application's state, React isn't prompted to re-render the component.
To hook the counter
variable on to the state, we can use the useState()
hook.
import { useState } from "react";
export const Counter = () => {
const [counter, setCounter] = useState(0); // the initial value
const increment = () => {
setCounter(counter + 1);
};
return (
<div>
{counter}
<button onClick={increment}>++</button>
</div>
);
};
The useState()
hook consists of a few parts to get all of this working.
Let's take a closer look at the syntax:
The useState()
hook can be thought of as encapsulating the following
declaration:
Where
v
is some value with the initial valuei
,f
is a function that updates the value ofv
at the timef
is called.
UseReducer
Suppose we had the following component:
import { useState } from "react";
export const Parity = () => {
const [counter, setCounter] = useState(0);
const [parity, changeParity] = useState(true);
const clickHandler = () => {
setCounter(counter + 1);
changeParity(!parity);
};
return (
<>
<div>{counter}</div>
<button onClick={clickHandler}>++</button>
<div>{parity ? "even" : "odd"}</div>
</>
);
};
In the component above, we have a click-handler that modifies two states — one that increments the value, and the other that changes the text from even to odd. For this sample component, the code above is fine. However, we can imagine a component with numerous states. And the more states we have, the more unwieldy our click-handler can get.
This is where the useReduce()
hook comes in handy:
import { useReducer } from "react";
export const Parity = () => {
const reducer = (state, action) => {
switch (action.type) {
case "increment":
return { counter: state.counter + 1, parity: state.parity };
case "changeParity":
return { counter: state.counter, parity: !state.parity };
default:
return state;
}
};
const [state, dispatch] = useReducer(reducer, {
counter: 0,
parity: true,
});
const clickHandler = () => {
dispatch({ type: "increment" });
dispatch({ type: "changeParity" });
};
return (
<>
<div>{state.counter}</div>
<button onClick={clickHandler}>++</button>
<div>{state.parity ? "even" : "odd"}</div>
</>
);
};
The useReducer()
hook has similar syntax to useState()
. The difference,
however, is that it gives us a better handle on state changes. The syntax:
In the syntax above, (the state object) is an object containing the states we want to mutate in our component. This object is bound to the identifier (the state identifier). To modify the state object, we use the reducer function This is a function with the following syntax:
Above, is the state identifier we used earlier. This is what gives the reducer function access to the previous state. The second parameter is an action object. This object essentially allows the reducer function to run through the list of states, comparing this object against each list entry (in our example, this is done with a switch statement). If it encounters a matching entry, it performs that entry's associated procedure.
To actually execute the reducer function, we call the dispatch function The dispatch function has the syntax where is the action object earlier-mentioned.
UseEffect
Arguably, the most powerful and oft-used hook is UseEffect()
.