00:00:11.780
Hey, everyone! What I've always loved about Rails is just how productive I feel when I'm using it.
00:00:20.369
Before Rails, I spent a lot of time working with Python, Django, PHP, .NET, and various frameworks. I like many aspects of them, but nothing has come close to Rails.
00:00:27.630
The combination of Rails as a framework and its language allows me to move quickly from an idea to a working version. While working in Rails, I don't feel like I spend a lot of time solving web development problems; instead, I focus on the unique issues related to the project at hand.
00:00:39.809
On the other hand, when it comes to mobile application development, I haven't experienced that same level of efficiency. I've spent time building native mobile applications in Java for Android, and although I appreciate the languages and the tooling, I often felt like I was spending too much time on basic mobile development problems.
00:00:52.020
Things like making API calls and persisting data—tasks that I assume every mobile developer has to solve—have taken me a significant amount of time. If you also consider cross-platform support for iOS and Android alongside a web version, it can require three developers to deliver features consistently across the board.
00:01:03.809
This has always led me to resist the push for native development, as it tends to lead to a substantial decrease in productivity. Over the years, I've sought after magic solutions, which, of course, don't exist.
00:01:16.409
I've built HTML5 web applications and deployed them with PhoneGap, as well as explored compiled native solutions like Appcelerator. While both approaches have their merits, I've not managed to create an HTML5 web application that feels genuinely native.
00:01:28.049
These applications feel more like web applications that are just marginally better on mobile. Compiler frameworks can also lead to challenges in testing and maintenance, diluting their supposed benefits.
00:01:39.060
However, about 18 months ago at Catapult, where I work, we began experimenting with React for our web applications. Initially, we made a lot of mistakes, but within a few months, we arrived at a stack we were quite happy with and successfully converted our front-end to React.
00:01:52.020
React is famously promoted by Facebook as 'Learn once, write anywhere,' instead of 'Write once, run anywhere.' I was skeptical of this claim initially because it felt like many platforms promised similar compatibility.
00:02:06.240
Nevertheless, we were pleasantly surprised to discover that most of what we learned while building React applications for the web carried over to React Native for mobile. It's possible to make the same mistakes on both platforms, but the architectural principles and libraries we used in React Web often applied directly to React Native.
00:02:18.420
This was fantastic because it meant any developer who was already familiar with our web codebase could quickly adapt and work on our mobile codebase, which was incredibly exciting.
00:02:30.240
From this point, we considered why we couldn't share our own code across platforms since libraries are essentially just reusable code written by others.
00:02:41.250
This led us to the realization that a significant amount of our business logic—that is to say, the majority of our code—could now be shared across entirely native applications as well as our web applications.
00:02:53.670
The outcome of this was that if we wanted to develop native applications and a completely separate web interface, the effort required to support three platforms could decrease to an additional 10% for each extra platform, rather than tripling our development resources.
00:03:05.730
As a result, I no longer felt like the person in the back of the room resisting the decision to pursue native development, fearing that we would need significantly more developers to accommodate that change.
00:03:18.090
So, I'm Ben, by the way. I'm the CTO of Catapult, a staffing platform based in London. I've spoken at conferences like this a few times before, typically covering topics related to Docker or server deployment.
00:03:25.770
Although I consider myself a full-stack developer, I feel much more comfortable setting up infrastructure or building APIs than I do with front-end development.
00:03:37.140
That changed when I started working with React. I want to give this talk because this React and React Native stack is the first time I really felt like the tools made me as productive building front-end applications as I am building Rails applications.
00:03:49.500
There are three areas I want to cover: First, a high-level overview of what a production React application looks like and its components; second, a discussion of the important considerations for our stack, especially regarding minimizing friction when sharing code between web and native codebases.
00:04:00.180
Finally, in a more detailed exploration, I’ve reflected on where I wasted the most time over the past 18 months and the sort of things I wish someone had pointed out to me early on.
00:04:12.660
The first important takeaway is that React is not a framework; it’s not like Angular, which includes almost everything necessary to build a front end or JavaScript application.
00:04:23.790
React essentially provides just a view layer. It offers a declarative model for defining components, allowing us to pass props and data into those components.
00:04:31.500
React then determines what needs to be rendered based on that input. When building for the web, this means using ReactDOM and generating HTML. For mobile, it involves using native components through React Native.
00:04:42.120
However, React doesn’t provide solutions for API calls or data persistence, which can be confusing when people refer to features as built-in React.
00:04:54.240
What is generally meant by this is that React is being used as a view layer alongside a collection of other libraries fulfilling the rest of the requirements.
00:05:07.200
As a Rails developer, I am somewhat spoiled because we often have a community consensus on what to use—if it’s not provided by Rails, there’s a well-accepted solution.
00:05:19.100
In the JavaScript ecosystem, however, that can lead to a very stressful environment, as you may be told to use one library this week and another the next.
00:05:29.940
What I'm going to present now is by no means a definitive React stack; rather, it’s one we have been very satisfied with and the one I've observed being used across various large React codebases.
00:05:41.220
This stack has proven to be very friendly, especially if your main goal is to maximize code reuse across web and mobile applications.
00:05:53.220
A typical React stack will need four main components: a Router, which in React terms serves to map URLs to specific components that need to be rendered.
00:06:06.420
In a native mobile application, the Router functions similarly; it recognizes when you want to display a login screen and determines which components should be rendered for that.
00:06:19.920
When discussing React, we'll also require an implementation of React for the web, which is ReactDOM, and for mobile, we’ll employ React Native.
00:06:31.000
Managing global application state in React is less straightforward, as React itself does not prescribe a standard way to do so. There are, however, widely adopted patterns in the community.
00:06:45.210
Many developers may refer to these concepts using the Flux pattern or Redux, which is my preferred implementation.
00:06:58.500
These patterns have emerged largely in response to the challenges in managing state within modern JavaScript applications.
00:07:10.999
In a typical JavaScript application, your state might be stored in a variety of models, potentially updated by various callbacks and API calls, leading to complex interactions that can create race conditions.
00:07:23.290
Using Redux, however, allows for a simplified and predictable state management system where the only way to change application state is by dispatching an action.
00:07:34.290
Actions describe the changes we want to make, and reducers handle these actions by accepting the current state and returning a new state based on the action's type.
00:07:46.840
This process creates a clear, linear approach to managing application state, providing us with a deterministic way to understand how our application is functioning.
00:07:59.210
By subscribing to state updates, different parts of the application, including the UI, can react to changes in state without directly altering the state.
00:08:10.350
This approach benefits from powerful tooling that allows us to step back through state changes, even rewinding to earlier states of the application.
00:08:24.076
Let’s look at a very simple example: imagine an app with a button that increments a counter. The counter’s goal is to reach the highest number possible.
00:08:39.820
We begin with an initial state represented as a JavaScript object with properties such as count, user name, and rank.
00:08:50.720
In a traditional JavaScript app, upon clicking the button, we would directly update this object. However, using Redux, instead of making this direct modification, we dispatch an action.
00:09:03.490
The dispatched action is a simple JavaScript object that contains a type property (e.g., ‘increment’) and its relevant payload.
00:09:15.570
We then pass both this action and the application's current state into a reducer function, which evaluates the action type and decides how to modify the state accordingly.
00:09:26.800
The reducer operates without modifying the original state object; instead, it creates a new object with the updated state.
00:09:40.100
The rest of the UI can subscribe to these state changes to respond accordingly, ensuring that actions are processed in a sequential manner.
00:09:53.500
Now we need to address side effects, a term that can be a bit confusing. When people refer to side effects, they usually mean asynchronous actions.
00:10:05.890
In our counter application, for example, if we wanted to maintain leaderboards due to its popularity, we would need to perform API calls whenever the button is pressed.
00:10:17.700
In this case, utilizing a library like Redux Saga can help us manage these asynchronous effects. Redux Saga listens for dispatched actions and can trigger the necessary API calls.
00:10:30.060
Once the asynchronous action is completed, Redux Saga dispatches additional actions to update the state with the latest information.
00:10:44.460
These four components—the Router, state management, the action handling, and side effects—form the foundation of our application architecture.
00:10:59.400
Next, we need to consider what aspects can and should be shared between web and mobile codebases.
00:11:12.130
Originally, I was not interested in creating HTML5 versions of native applications because I hadn't encountered many production applications where the web version was simply a scaled-up version of the mobile one.
00:11:24.090
My assumption has generally been that the view layers of mobile and web applications would differ significantly in structure and layout.
00:11:39.240
For instance, a form that takes up one screen on a web application may require three, four, or even five separate screens on mobile. Therefore, I have avoided sharing the view structure.
00:11:51.270
This thought process aligns with how we build Rails applications. If we develop a web application and decide to add a JSON API, we keep the business logic in models, service objects, and maintain a separate, clean view layer.
00:12:02.760
We wouldn't consider mixing a single view for a JSON API with a traditional HTML view, as they serve very different purposes.
00:12:14.310
Applying this reasoning helps not only maintain structure but also maximize efficiency when sharing code. The critical pieces tend to revolve around state management and side effects, which can easily be shared.
00:12:26.390
Thus, we see significant benefits in sharing those components.
00:12:37.440
For practical examples, nearly every application includes a login page. It begins with a Router that dictates which components to display.
00:12:49.620
The view layers will likely differ across web and mobile platforms, but there are commonalities when addressing the login button's functionality.
00:13:01.440
Regardless of the platform, the action that occurs when the login button is clicked boils down to dispatching an action aimed at updating the application's global state.
00:13:15.840
We would want to set flags that indicate the app is processing a login, allowing the UI to display loading indicators as necessary.
00:13:27.630
Simultaneously, we would initiate an API call to login the user, using our side effects to manage the login action.
00:13:42.240
This API call will return a success or failure. When successful, we should update our state accordingly, hide the loading indicator, and store any related data such as the login token.
00:13:55.470
Ultimately, we want to redirect the user to a dashboard or home page, and this logic may need to interface with the Router.
00:14:09.330
This is where the separation of concerns begins to blend because the side effect has to interact with components that aren’t shared.
00:14:23.550
Thus, we may end up needing an if-condition for web and mobile or disconnecting the side effect entirely from that logic.
00:14:36.300
At the beginning of this login flow, when the login button is pressed, the view component contains both the button handler and callbacks for success or failure, which can inform the Router to carry out proper navigation.
00:14:52.730
This keeps the side effect shareable while allowing the component to know how to interact with the Router.
00:15:04.540
Ideally, following this pattern allows us to separate our business logic while sharing actions, reducers, and side effects, creating a structure akin to that of a standard React application.
00:15:17.860
This has made it much easier for developers to transition between mobile and web development; they can contribute seamlessly to one or the other.
00:15:30.300
I would like to clarify that there is indeed a level of complexity involved, especially regarding native functionality, such as when a mobile app wants to access device GPS.
00:15:43.170
In such cases, asynchronous behavior is required, making it difficult to create a genuinely shared component.
00:15:56.380
I learned the hard way that while it’s important to strive for shared operations, sometimes it's necessary to tailor solutions for unique functionalities.
00:16:09.780
This point emphasizes the way we divide the overall architecture of the application, balancing standardized approaches while allowing exceptions.
00:16:21.390
Fundamentally, the goal is to build maintainable and scalable applications where the shared code makes overall development more efficient.
00:16:35.340
Before wrapping up, I want to stress that following the outlined patterns for organizing code has created a productive development environment.
00:16:47.750
This has made it so much easier for us to onboard new developers since most tutorials available for React web will be applicable to React Native.
00:17:01.620
This familiarity reduces the learning curve and complexities, leading to smoother transitions in projects regardless of the specific platform being worked on.
00:17:17.430
What I want to share with you today largely covers architectural insights and practical advice from my experience.
00:17:29.060
The productivity gains we’ve seen using this stack have shifted my thinking significantly about when to start integrating native applications into projects.
00:17:45.390
There's an ongoing shift to what we realistically expect when supporting both web and native applications, so the changes in approach have become valuable.
00:17:58.890
I’ve gone ahead and created a sample project available at cookie-dough.co.uk/railsconf2017 that illustrates this sharing in practice. It includes both iOS and web components along with a Rails API.
00:18:10.740
It's open-source, allowing anyone interested to see its workings and explore which parts resonate and which don’t.
00:18:24.480
As we wrap up, I want to mention that at Catapult, we are hiring! If you are intrigued by what we've discussed, please reach out to me at [email protected], or let’s grab coffee if you're around London.
00:18:42.540
Thank you for listening. If you have any questions, I’ll be happy to answer them now.
00:19:02.090
Absolutely! I encourage everyone to ask your questions.
00:19:16.350
When it comes to organizing actions in large files, I’m fond of using Redux Source, which is syntactic sugar on top of Redux.
00:19:24.720
It provides a framework for breaking down multiple reducer files while maintaining proper structure.
00:19:36.920
Great question! To support responsive interfaces for web versions, I initially swayed towards a framework like Bootstrap.
00:19:49.610
It simplifies creating mobile-friendly web applications, greatly reducing the complexity involved.
00:20:01.620
In parallel, Microsoft has released a framework that permits component definition and chooses between web and mobile layouts.
00:20:15.680
How do we handle customization in platforms with diverse components? Honestly, I don't see many display discrepancies.
00:20:29.820
Generally, it’s good to lean on React as it handles platform-specific components very well.
00:20:44.560
Is service architecture beneficial over old server-side Rails applications? Initially, Rails is quite rapid for prototyping.
00:20:58.260
Yet, over time, adopting API servers alongside client-side architectures yields greater maintainability.
00:21:11.950
Despite the initial speed of Rails, I've found the efficiency and structure of separating APIs and front-end heavily advantageous.
00:21:26.800
When it comes to developing native applications, learning the SDK is essential, but the React Native CLI streamlines this process.
00:21:40.680
React Native provides a minimal setup to help kickstart projects without excessive boilerplate.
00:21:54.590
As a Rails developer, dealing with boilerplate can be annoying, so I leverage tools like Ignite to automate setups.
00:22:09.570
This ensures consistency and organization from the get-go, allowing for easier extraction into NPM packages.
00:22:23.020
Lastly, I want to emphasize the importance of managing Babel configurations across projects.
00:22:38.240
Babel is a tool that helps transpile modern JavaScript to make it compatible with older browsers, which is crucial for React web applications.
00:22:52.070
However, there's a risk of Babel configuration conflicts if not managed carefully, especially when integrating with React Native.
00:23:06.360
Realistically, changes to Babel can lead to sporadic runtime errors, so consistent management of the cache is essential.
00:23:21.050
I hope this overview captures the critical elements of the architectural decisions we’ve faced while integrating these technologies.
00:23:35.500
Ultimately, the changes to our processes have greatly influenced productivity and adaptability for new features.
00:23:49.490
I invite any final questions before we wrap things up. The shift in mindset has been transformative.
00:24:03.390
What I want to underline is the vital and positive shift we’ve experienced, allowing us to integrate diverse platforms seamlessly.
00:24:20.150
Once again, I want to thank you all for participating in this session. It’s been enlightening to share these experiences with you!