-
-
Save nateabele/eace26da2c7851086a5d18f45dda612c to your computer and use it in GitHub Desktop.
Forked in response to this article: https://medium.com/let-s-learn/lets-learn-composing-react-components-with-ramda-5db457997554 — I'm not sure I translated the code 100% correctly, but, you get the idea.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This is a forked and annotated example. I think it gets a lot of stuff right in terms | |
* of best practices on composition. | |
* | |
* There are a few things that I think could be better organized... that doesn't mean that | |
* the author is wrong; the things I'm calling out probably aren't even related to the point | |
* of the article. | |
* | |
* One of my issues with React is that it encourages decomposition for no good reason, which | |
* just makes your UI harder to maintain because you have to piece together dozens of tiny | |
* little functions in your head just to understand what a single logical interface element | |
* looks like and does. | |
* | |
* Deciding where to break things apart is just as important as deciding where to keep things | |
* together. | |
*/ | |
// A single atomic unit of UI — this is good | |
const Comment = ({ text, author, _id }) => ( | |
<div key={_id}> | |
<p>{text}</p> | |
<h5>{author}</h5> | |
</div> | |
); | |
// This is probably not worth calling out as its own thing, since you can easily inline it, | |
// and inlining it will probably lead to better code clarity | |
const CommentList = map(Comment); | |
const CommentWrapper = children => ( | |
<div> | |
<h2>Comments</h2> | |
{children} | |
</div> | |
) | |
// This is nitpick, but Functor Laws say you can write `compose(map(x), map(y))` as | |
// `map(compose(x, y))`, which feels a bit less noisy | |
const wrapComments = compose( | |
map(CommentWrapper), | |
map(CommentList) | |
) | |
// These two don't really buy you much, IMO... just inline them | |
const getComments = compose( | |
wrapComments, | |
map(prop('comments')) | |
) | |
const CommentsWrapper = children => ( | |
<div style={rowStyle}> | |
{children} | |
</div> | |
) | |
const wrapAllComments = compose( | |
CommentsWrapper, | |
getComments, | |
) | |
const Comments = compose( | |
wrapAllComments, | |
prop('posts') | |
) | |
/** | |
* Here's an example of the above that's functional but still cohesive. | |
* | |
* Still the same level of flexibility, and the composition is just | |
* as effective, but the flow is much clearer since we're not trying | |
* to do FP for its own sake, but mixing it effectively with the | |
* semi-imperative style that React forces on us. | |
* | |
* Using simple continuous left-to-right composition with the `pipe()` | |
* function, we can do a simple inline flow that only calls out to | |
* `Comment` and `CommentWrapper`, which are exactly the two atomic | |
* units of view code. | |
*/ | |
const Comments = pipe(prop('posts'), (posts) => ( | |
// This is even a bit superfluous. --^^-- You can just do `Comments = ({ posts }) => (` | |
<div style={rowStyle}> | |
{posts.map(pipe( | |
prop('comments'), | |
map(Comment), | |
CommentWrapper | |
))} | |
</div> | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment