jwstudent
Posted on September 13, 2020
So I was trying to make a super simple app with react and firebase, using typescript. Basically an app that lets you login to see some authorized content.
The application state for this POC was extremely simple:
{
user: { id: string, name: string },
isAppReady: boolean
}
Initially the backend was going to be written with .NET so I used their CRA (create-react-app) template, but later I switched to java. Anyway, like magic, I had an app that worked and even communicated with a backend. Yay! I noticed that there were a like a billion dependencies in this web project, but surely I wouldn't have to understand a billion libraries for this simple project and I could clean them up later.
Next, I added the firebase dependency to my web project and configured it with my backend instance and everything was still good.
Then, I created a LoginButton that let me login and added it to the nav menu. I figured out how to share objects via context providers and I was able to prompt for login. Woo!! I'll be done in no time.
Now, I have used JS for many, many years, but unfortunately, was late to the whole ES2015 fiasco while working at a large company for a few years (staring in 2014). But when I came back to the real world, I was hit with a rude awakening as to the state of front end development.
But you know, that was no biggie. Things change, and I got back up to speed and started working on a SPA app for another company (I wasn't a NOOB to SPAs then, nor am I now). I didn't choose the framework there, but I used one whose reactivity model was based on browser events (with a manual hook available), so it was pretty simple to work with.
Later I migrated to vue and it was simple to work with. Now in deciding between using vue, angular, or react, I previously eliminated react because the last time I started creating a react app, I had to install like a billion dependencies just to do something simple, but now for personal growth I decided to give it a try again.
Ok back to the app. I don't want this to be a rant, but let me explain what happened next. I noticed that the user was null on startup because firebase.auth
does not completely initialize on creation. So I needed to update my app's state when firebase became ready. And this is where everything fell apart.
First, I'm like "it's time to REALLY be a react dev". Cool. Since I needed state management, I needed to learn redux. This was expected. But then redux is not react-specific, so I needed to learn react-redux, ok..., which sent me to redux-toolkit. Then because I was dealing with asynchronous logic, I had to learn what a Thunk was and how redux-thunk works. So now I'm like... seriously, wtf. Now sure, maybe I could have abandoned this architecture and started using MobX or something, but I feel like react/redux is the standard for react. Maybe I'm wrong.
Ok, so I have to learn all of this and organize it in my mind so that it makes sense so that I can actually use the acquired information. And that's way too much complexity IMHO for such a simple task. After I use up my mental bandwidth learning how to update a user asynchronously, I fight with the type annotations because in my starter cra template (remember .NET), I guess the people at MSFT also did not fully understand the interaction between the libraries/frameworks (ie react / react-redux / redux-thunk). There was a bug in the code, however, instead of fixing the root issue, they simply removed type checking by asserting the component as any
.
export default connect(
(state: ApplicationState) => state.weatherForecasts, // Selects which state properties are merged into the component's props
WeatherForecastsStore.actionCreators // Selects which action creators are merged into the component's props
)(FetchData as any);
Who knows why they worked around it that way, but there was definitely an error with their typings. I was confused as to why I was getting compilation errors when I tried to connect but figured out they were incorrectly typing their components.
I digress. So I correctly type my component, so that I can connect, so that I safely dispatch my action, so that I can update my state, so that the component refreshes. But now I am aware that I have to repeat this process for every new component that communicates with the store. Mainly update the component interface, create the actionCreators and reducer separately, then link them (with a switch/if/etc in the reducer), and manually connect the component to the store.
So that's not DRY and is unacceptable because I don't have to do that in other frameworks. For example all components can use the store with this one line in vue
Vue.use(Vuex);
This is not to promote vue, but to say I feel like the time to actually do something efficiently and correctly with react is way higher than with other frameworks that I've used and I feel like I've stepped back in time. Since I know I could write something to eliminate this duplication, I'm sure there's a better way. But again the problem is not that it can't be done, but that the time to do it efficiently and correctly is higher.
I haven't even started applying middleware to my routes and error handling, but if I have to include another 4 libraries just to add access control to my routes (before actually implementing access control), then I must say, its starting to look like react development is slow and tedious.
I'm curious as to what others think.
Posted on September 13, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.