I am currently learning functional programming using HyperappJS (V2) and RamdaJS. My first project is a simple blog app where users can comment on posts or other comments. The comments are represented as a tree structure.
My state looks something like this:
// state.js
export default {
posts: [
{
topic: `Topic A`,
comments: []
},
{
topic: `Topic B`,
comments: [
{
text: `Comment`,
comments: [ /* ... */ ]
}
]
},
{
topic: `Topic C`,
comments: []
}
],
otherstuff: ...
}
When the user wants to add a comment I pass the current tree item to my addComment-action. There I add the comment to the referenced item and return a new state object to trigger the view update.
So, currently I'm doing this and it's working fine:
// actions.js
import {concat} from 'ramda'
export default {
addComment: (state, args) => {
args.item.comments = concat(
args.item.comments,
[{text: args.text, comments: []}]
)
return {...state}
}
}
My question: Is this approach correct? Is there any way to clean up this code and make it more functional? What I am looking for would be something like this:
addComment: (state, args) => ({
...state,
posts: addCommentToReferencedPostItemAndReturnUpdatedPosts(args, state.posts)
})
Ramda is intentionally designed not to modify user data. Passing something by reference won't help; Ramda will still refuse to alter it.
One alternative is to see if you can pass the path to the node to which you want to add the comment. Ramda can use a
pathwithlensPathandoverto make a version that will return a newstateobject, something like this:Here the path we use is
[1, 0], representing the second post (index 1) and the first comment (index 0) within it.We could write more a more sophisticated lens to traverse the object if the path is not enough.
I don't know if this is an overall improvement, but it's definitely a more appropriate use of Ramda. (Disclaimer: I'm one of the authors of Ramda.)