ch20-TheBoys-ramda-with-react
Before starting this section, make sure to go through the prerequisite where were mirror Heroes to Boys.
Why Functional Programming, why Ramda?
If we take a look at the recent proposals to EcmaScript, we will realize that FP is the way and that new JS features will closely resemble RamdaJS utilities. Check out So what's new in ES2025 for a premier. We believe that learning FP and Ramda today will build a future-proof JS skillset, at least for the next decade.
We have a React app with solid tests built by TDD at all levels and 100% code coverage. In the prerequisites we mirrored Heroes group of entities to Boys. Now we can try out daring refactors and see if things still work.
While the literature on FP is plenty, there is a lack of real world examples of using FP and Ramda, especially with modern React and TypeScript. We are hopeful that this section addresses that gap.
Functional Programming JS resources
Here are the resources that inspired this work. You do not have to be fluent in FP to benefit from this section, but if you choose to back-fill your knowledge at a later time, these resources are highly recommended.
Install the Ramda package
In the following sections we will give practical examples of using Ramda, then apply the knowledge to 3 Boys components; BoyDetail.tsx, BoyList.tsx and Boys.tsx.
Create any function with n arguments and wrap it with partial . Declare which args are pre-packaged, and the rest of the args will be waited for. In simple terms; if you have a function with five parameters, and you supply three of the arguments, you end up with a function that expects the last two.
Where can partial apply in the React world? Any event handler, where an anonymous function returns a named function with an argument. The below two are the same:
handleCancel will wait for the click event - onClick={handleCancel} - and navigate to /boys route.
Similar applications of partial are in Boys.tsx
No FP premier is complete without curry. We will cover a simple example here, and recall how we used classic curry before.
How is currying used in the React world? You will recall that we used currying in Heroes.tsx, HeroesList.tsx, VillianList.tsx VillainList.tsx components. We did not use Ramda curry, because it would be too complex at that time. You can optionally change them now.
Let's make sure to use Ramda curry in the BoysList.tsx components for sure.
Takes 3 functions; predicate, truthy-result, false-result. The advantage over classic if else or ternary operator is that we can get very creative with function expressions vs conditions inside an if statement, or a ternary value. Easily explained with an example:
Where can ifElse apply in the React world? Anywhere where there is complex conditional logic, which might be hard to express with classic if else statements or ternary operators. The example in BoyDetail.tsx is simple, but good for practicing Ramda ifElse.
Here is the BoyDetail.tsx component after the above changes. It can be compared to HeroDetail.tsx side by side to see the distinction between using Ramda and classic array methods.
pipe is the bread and butter of Ramda; it is used to perform left-to-right function composition - the opposite of compose which is right-to-left. Explained with a simple example:
Where can pipe apply in the React world? Any sequence of actions would be a fit.
The below 3 varieties of handleCloseModal function from Boys.tsx are equivalent.
The below 3 varieties of handleDeleteBoy function from Boys.tsx are also equivalent.
The below 3 varieties of handleDeleteFromModal function from Boys.tsx are also equivalent.
Here is the Boys.tsx component after the above changes. It can be compared to Heroes.tsx to see the distinction between using Ramda and classic array methods.
Refactoring search-filter with array methods to Ramda
For this example, we will extract search-filter logic in BoyList.tsx into a standalone TS file, alongside types and data. Given the data heroes array, we want to filter by any property (id, name, description) and display 1 or more objects. To accomplish this, we use 2 lego-functions searchExistsC & propertyExistsC that builds up to searchPropertiesC, C for classic.
Although we have partitioned the functions like legos, the logic isn't very easy to follow while reading the code. The reason is having to use multiple arguments, and how the data changes from an array to an array item (an object), while also having to change the placement of the arguments. Using Ramda, we can make the code easier to read and use.
Here is a quick comparison of .toLowerCase() vs toLower() and .indexOf() vs indexOf. Instead of chaining on the data, Ramda helpers indexOf and toLower are functions that take the data as an argument:
someData.toLowerCase()vstoLower(someData)array.indexOf(arrayItem)vsindexOf(arrayItem, array)
We can refactor searchExistsC to use Ramda. Here is the before and after side by side:
Below is a quick comparison of array.find vs Ramda find, Object.values vs Ramda values. Notice with Ramda version how the data comes at the end, with this style we can save an arg for later.
Here is the refactor of propertyExistsC to use Ramda, before and after side by side. We can amplify the Ramda version with pipe to make the flow more similar to the classic version.
Our final function searchExistsC uses the previous two. Here are the classic and Ramda versions side by side. We can evolve the Ramda version to take 1 argument, and get the data later whenever it is available.
Here is the full example file that can be copy pasted anywhere
With that, we can replace searchProperties and the lego-functions that lead up to it with their Ramda versions.
Here is the comparison of the key changes:
Here is the final form of the component:
Our tooling and tests are still intact after the changes, and that is the luxury of having very high coverage. We were able to apply daring, experimental refactors to our code, and are still confident that nothing regressed while the code became easier to read and work with using FP and Ramda.
To take a look at the before and after, you can find the PR for this section at https://github.com/muratkeremozcan/tour-of-heroes-react-cypress-ts/pull/110. You can also compare the Boys group of files to Heroes or Villains.
Last updated