Building a React/Redux Application with a Ruby/Rails backend
First, I want to start by saying that I really struggled with wrapping my mind around redux. React felt very straightforward and conceptually made sense to me. So it would seem that I would pick up redux quickly but that wasn’t the case. For some reason, I just didn’t get it. This made for a very frustrating project build. I was constantly thinking of ideas as I was trying to decide on a project, however, I would remind myself that I didn’t know how to actually implement that idea. I realized I just needed to go for it and I’ll figure it out one step at a time. So that’s just what I did!
I decided on creating a phone calling application. Just like what you have on your phone. You can dial a number, start a call, end a call, and view your call history. Of course, it doesn’t actually call the number but that may be something I’ll pursue in the future using an API such as Twilio.
Here’s the end result of the phone:
Here’s the end result of the history:
I’m going to focus on talking about the React/Redux side of things though as I already covered Ruby/Rails in a previous post. I specifically want to focus on the reduxjs/toolkit so I’ll briefly discuss the other aspects of the app ahead and then I’ll get more detailed in the store and reducers.
The UI for the history is very simple. There’s a history container that will display a callHistory component for each call currently stored in the database.
Welp, that’s pretty much that for the Call History. Next I’ll talk about the phone piece and then we’ll get into the juicy stuff!
The phone was a bit more intricate. As with the History, there’s a phone container, but this will produce a component for the phone number display (KeypadDisplay), the number pad (Keypad), and then the start/end call buttons (Buttons).
The KeypadDisplay simply uses the react-redux “useSelector” to get call progress and call phone number state from the store. The Keypad has the dialpad and uses the react-redux “useDispatch” to send the number value of the button to the store.
Finally, the start/end buttons. There needed to be a lot of functionality behind these buttons. When starting a call, there needs to be a timer that’s fired and counts/dispatches the seconds to state for the duration. When ending a call, the timer needs to be stopped. Simple enough? Except when you consider handling user input errors. What if a call is ended before it was ever started? What if a call is started without a phone number? A big solution here was creating a new status boolean and keeping it in the store. The default state is set to false and the boolean is flipped depending on whether the start or end call buttons are used. A call can’t be ended while false. In addition a call can’t be started when true.
Knowing what I know now, this would have been easy. Except I ran into a really time-consuming bug here. I initially tried to keep status locally in the component but whenever the component was being re-rendered due to state change, the boolean was getting reset back to it’s initial false value. There’s also another HUGE issue that I haven’t been able to figure out at the time of me writing this. clearInterval DOES NOT STOP THE TIMER! I’ve been through the documentation and through the deepest corners of stack overflow to resolve this but have been unsuccessful. The good news is that it doesn’t hurt anything other than performance.
I’ll update this blog once I find a solution. That basically covers this side of things so, next and finally, we’ll move into the store, reducer, actions, and fetch requests.
There’s not much of a change when it comes to creating your store. The store is created with “configureStore” now opposed to “createStore”. The biggest difference that I want to spotlight is that you don’t need to configure the store to work with the redux browser extension and it’s already shipped with default Middleware! Woohoo!
Slices were the biggest motivator for me to learn and use the redux toolkit! Some of the main reasons is that it was cleaner to me. You can mutate state in your reducers when using slices! Slices use Immer to make copies for you so you no longer need to write up a bunch of cumbersome code to write copy after copy! Slices allow for extraReducers that will respond to other action types! Finally, it automatically generates action creators and action types that correspond to the reducers and state!
How awesome is that?!
The final piece that’s needed is the fetch requests. This required an import of “createAsyncThunk” from the redux-toolkit. Remember that default middleware is already available.
The Redux documentation writes these out using AJAX requests but a requirement for we was to specifically use fetch so this took a little time to figure out and get working but in the end it worked very well.
There’s much more that I have to learn regarding the redux-toolkit and Redux in general but this project helped immensely! I really pushed myself by using the redux-toolkit since this wasn’t included in anything I had learned previously. Everything has been simplified and I found that everything made much more sense to me this way. Also, no more connect, no more mapStateToProps, no more mapDispatchToProps, no more passing props around the entirety of your app. I would highly recommend the redux-toolkit to anyone getting into Redux!