Is React functional programming?
This is an episode of The Eric Normand Podcast.
Subscribe: RSSApple PodcastsGoogle PlayOvercast
Even though React modifies the DOM, it is considered functional programming. It's not strictly functional, but it is easy to reason about because the DOM is its only output. FP concepts can help us understand React.
In this episode, we explore the FP concepts that React employs and how reactive programming relates to functional programming.
Eric Normand: Is React and that whole style of UI framework, is that really functional programming? Hi my name is Eric Normand and these are my thoughts on functional programming.
A curious listener to the show asked me about Re-frame. Re-frame is a ClojureScript framework built on top of React where there is a global immutable database.
The various components that you write can access that database, the global database, and then it access it through subscriptions. Whenever that subscription changes, whenever the data that comes through that subscription changes, the component is re-rendered.
The question was, is that really functional programming? Shouldn't you use pure functions, like take the whole database, render out the entire UI from that as a pure function, as opposed to whatever this re-frame thing is doing?
It's not just a pure function, it's got these subscriptions that are a new input besides the arguments and they have an output which is that they're rendering at basically random times. Is this really functional programming?
It's a good question. To me, what this question indicates is that the person has not developed UIs before. UIs are notoriously stateful. You have stuff like whether a drop down is open or while you are typing, there's different values changing inside of the text box.
You could have each of those strings that are in there as a new value and it's constantly changing. You press a button and it does something, then it's now has a loading spinner after you press it. It's got a lot of state.
Functional programming has this big challenge of how do you deal with the state? One approach is to just say we're going to render the whole thing as a pure function.
We're going to get all the state. We're going to gather it all up. We're going to put it in one place, one big value and we're going to pass it to one function. It's going to call other functions.
It's going to take the pieces of data, pass it to the next thing, pass it to the next thing, until you get to the trees where a lot of the HTML gets generated or the React-DOM nodes, whatever you want to call it.
Now it gets generated. You have to thread all the data that it needs through. That's certainly possible, you could do that. The big issue with that — from a practical point of view — this is not from some framework chauvinism or anything like that. From a practical point of view, what happens is your UI, it has very little to do with any rational data model, any kind of rational semantic meaning model.
For instance, you could have a design, wherever it came from, maybe a designer drew it up that has a big content pane on the left and then a sidebar on the right. Somewhere in your big function, you have to split because there's a div on the left and there's a div on the right and they have to split.
There's no way to have half, the top half and bottom. No, it's left and right. The contours of the HTML has to match the contours of the functions.
Then you say while we're going to have...The username is going to be somewhere in the right sidebar. You need to thread the name of the user into the right sidebar which will then put it in a label with their username inside of it.
You got this data thread. You'd imagine it's going through onto the right sidebar side. Somewhere deep inside the right sidebar, there's a div that has the name of the user.
There's a change. They do some testing where the user can't find their name on the panel. We need to move it to the main content page and make it really big, something like that. Now what you need to do is thread that username into the left side, which is the content pane.
All that work of threading it through and say I'll pass the username in, pass the username in, pass the username in, OK now you can display it. You have to do that again on the left-hand side and pass the username through, pass it. About every component knows the username now. It's essentially global information.
You've had to do all this work where the left content pane, the semantics of it as a UI layout component, are very agnostic to what is displayed inside of it. Notice that that meaning has not changed even though you moved something from the right to the left. It's a container.
You've had to change the arguments. In theory you could have, but in this situation, you couldn't just copy-paste that component, cut it out of the right-hand side and put it into the left-hand. You can't just do that because you also had to do all the threading. All that threading. This happens a lot in UI.
It's not just because your designer got it wrong at first, it's you don't know what the good design is going to look like until you do it, and you test it, and you show a user.
There's no rational model of this belongs here because that's what it means. You've got one, then the rational model will have to change because it's just wrong. You did an experiment, you made a prototype, you showed it to the users, and it wasn't good enough. It wasn't getting the users' user experience, the user's behavior. It wasn't given the right behavior from the user.
The solution to this problem is that you want the components to be able to get that essentially global information on their own without having to thread it through. That way, you could just copy-paste it over.
You don't have to worry about threading. You don't have to worry about, "This parent component has to know everything inside of it." No, you don't like that. That's not good. That's coupling.
You couple the whole UI to the structure of the HTML. All of the components, they care about the HTML, you do not want that. This is something that I have come to believe after years of working in UI, that that is just not good. You want the component to be relatively independent.
You give those components access to this global information. You store all the data outside in one spot. It's got to be mutable because the state does change. You store it all. When I say all, there's caveats to that. You store all of it outside, let's be simple for a second.
Then, you give them selective access. You could say, "OK. This is a username component and wherever I put this username component, the contents of that component could be made the current user's name." How does it get the current user's name? It subscribes to that value in the database.
Now you've decoupled the location of that element from its function. The CSS could mess things up, there's complications here. In theory, you could take this component and just move it around your UI. None of the components that it is inside of have to care. There's no dataflow issues anymore.
The thing is, because you're getting it from this global place, it could change. That's where you're storing all your state, and state changes. Now, the UI needs to change based on what's in that big global store, the database.
This is another problem because you don't want to re-render the whole thing. If you only need to change one or two divs, you just want to change those one or two divs. You want some kind of locality of the change.
What React gives you, what Re-frame gives you, is this idea not of functional programming but of reactive programming. You should think of it like this component is reacting to changes to the database and always staying current. Always showing the most recent value.
It's not functional programming in the sense of everything is a pure function on data. That's a non-starter with UIs, for all the reasons I've explained. It gives you something else which is that you don't have to worry about what is being displayed anymore.
It's always the current value. I change data in one spot, and my UI is up to date. That's a big promise and Re-frame delivers on that.
React should deliver on that too, but there's problems with that in the way people actually practice their React. We can go into that in another episode.
This is reactive programming and it borrows a lot of ideas from functional programming. The extra idea is that in addition to the arguments to the function — in a pure function, you just have the only inputs to that function. The only thing that can affect the output value of that function are the arguments.
You change the arguments, you can change the output. If you have the same arguments, you're going to get the same output. This adds inputs. It adds those subscriptions. It adds this thing that it can react to, so this is changing data.
It's not just arguments. You should think of it as like static. These are things that can change. Because the components, if you do it the right way in this reactive programming way, reactive UI way, the only output of those functions is UI.
It's not like sending Ajax requests. It's not doing an alert. It's not changing something somewhere else, because you don't know when it's going to render. Anytime the database changes, it could re-render. The only thing it should do is render.
That's what something like Re-frame gives you in at its best. If you do React in this way, it gives you this really nice way of doing UIs. It's not strictly functional programming, but it gives you good promises anyway. Promises that you'd want as a UI designer.
I don't think I could do complex UIs in the way that I used to anymore because I got really tired of it. I used to do it with JQuery back in the day and it would just get really complicated. A lot of the times I would just wind up re-rendering a big tree because it's too complicated. I know that there's going to be some weird corner cases and I just let me re-render the whole thing.
I don't want to base on the history. I know what it needs to be now, let me just make it that now. React delivers on that. It does it much better than you can do in JQuery because of the virtual DOM and everything.
Then, because you've got these components that can re-render independently, you also got that locality that if you need it, you could make a table that had 10 million rows in it. You can't really render a page that big, it would be too slow, but you only have to render what's visible, right?
It gives you this ability to only update the stuff that's visible and ignore all the stuff that you got the data for but you don't want to make a DOM for it.
Anyway, I'm digressing. I like React. The new Angular view, they're pretty much the same. The issue is how you program in it.
Are you programming that you do only have...It's not functional programming. It's not just arguments and a return value. It's not pure functions. It's arguments and changing input. Like a subscription, you redux. If you're doing reactive redux, it could be redux updates. The only output is the rendering.
It's different, but it's got a lot of the same ideas. You can borrow the same terminologies, stuff like that.
All right. I am Eric Normand. This has been one of my thoughts. Please find me on Twitter @EricNormand. Or on LinkedIn, search for my name in Clojure if I don't show up first. I'd love to get in discussion with you.
All right. See you later. Goodbye.