UI making has a lot of challenges no matter the platform you are working with. I have worked with web solutions in the past and know the struggle of getting:
- Same behavior across browsers, even the same browser across versions (looking at you IE > :/ )
- Sizes, fonts and what-not not working as expected
- Browsers ignoring W3C suggested standards and more...
Those kinds of problems came with workarounds, fixes, hot-fixes or tools. The first one that I remember was jQuery - almost the same code execution across browsers. For the styling issues solutions like Bootstrap and Foundation were born. Implemented toolkits like normalize.js and CSS3 modules as founding parts (and let's not talk about CSS2 times).
Previous solutions like jQuery have in mind changing DOM elements without worrying about controlling the general app state. The next step was done by Angular 1 with the inclusion of directives and Adobe Flex (AS3) where you could have double binded values. Now you can put some variable inside your html text input element and automatically change the value inside your model/controller when you edit the input value. That way of developing web-apps can lead to debugging and stability issues in the long run (and it did).
I worked with Adobe Flex around 2011 - 2013 for desktop (OS X) and mobile (iOS) solutions. The cool part was declaring components using xml and handling logic in a structured way because flex already had methods like render, onLayoutUpdate and others to control your component at runtime. Seeing the same component I styled in my IDE preview working for desktop and mobile was magical. I was in heaven; worrying less about styling and focusing on getting the actual job done. Sadly flash was killed a few years later.
Even then I noticed that it was easier to control the application logic forgetting any bidirectional flow and just making the parent disperse the actions to their children components.
After that I lost interest in front-end development and focused on back-end. A few years later I was introduced to React for a web project and felt familiar with the workflow and was excited for the new paradigm.
When you develop using React, first of all, you need to understand mutable vs immutable elements because React is just a library for building user interfaces and is used almost always with redux.
The React (and Redux) proposed paradigm works like a finite-state machine (FSM). Given a set of parameters to some function, you can expect the same result every time: the so called pure functions. This one-directional paradigm offered an easier way of dealing with app state and made debugging and developing applications easier.
The most basic type of component in React is a function component.
This type of component is used only for presentation purposes and does not include any logic.
Class components are used to handle logic and/or local state changes. For example: we are going to use the
Label component to show some user’s info.
In this example we are fetching server data, like a real world app, and rendering it using the
Label component. The order of the events goes something like this:
- Set the initial value for
userNameas an empty string.
componentDidMountis called after component is mounted.
- Render event is done using
userNamewith empty value.
- Server fetch is done.
UserInfostate is updated.
- The new
userNamevalue is rendered.
As a side note, class components were the first type of component in React and their use is not recommended for new projects. See React hooks.
In a real world project you need components for certain purposes. Some common types are:
- Stateless: Something like a label or button. This component behaves the same way every time. It holds no information.
- Local: Something with local state that can be reused across the project (e.g.: Registration form). It does not depend on the application state. It holds its own information.
- Unique: Something like a screen component that can both change application state and can have local state.
So what is a store? Well, from the Redux docs:
A store holds the whole state tree of your application. The only way to change the state inside it is to dispatch an action on it.
That means you have and Object with properties that are changed after an action is dispatched. Like this:
It's that simple most of the time. For scalable stores this read is recommended.
Sometimes, you need to pass a value between various nesting levels and you can't save it in the store (e.g. a function handler). The naive approach is to add a prop to every level of nesting. But there is a better approach.
That’s where Context can be helpful. You can pass data through the component tree without having to pass props down manually at every level.
As you can see code is cleaner and is using the value only where it's required. This can improve development experience making the code easier to read/maintain.
Disclaimer: In some cases it can be better use react-navigation instead.
Besides class components you can have a stateless component with state (yes, sounds confusing at first). You can achieve this with React Hooks.
Hooks were introduced because:
- It’s hard to reuse stateful logic between components.
- Complex components become hard to understand.
- Classes confuse both people and machines.
So let's see what a hook looks like:
What's happening here is we are using 2 hooks:
useState returns a stateful value, and a function to update it.
count is the value.
setCount is the function to update it.
useState can receive an optional default value,
initialCount in our example, and button onClick increments our count:
setCount(count + 1).
useEffect is used to detect value changes. In our example we can detect parent component changes in
initialCount and update our local state
useEffect can be used to handle
componentWillUnmount events too.
There are more hooks but let's keep it simple. What I like about hooks is they reduce boilerplate code and make it easier to understand a component's logic.
Remember Adobe Flex IDE component preview from the start of the post? Well, it's not a secret that the same feature is available in Android Studio, Xcode, etc. But since we are working with React, we need a js-based solution.
This is where Storybook does its magic. You can add stories for your component and make UI development a lot faster.
Storybook works with stories (duh to myself). Stories are simple to create, for example:
We have a Toast component with 2 possible states: success and error, and that's it. We can run our app in storybook mode and see these stories in action in a real device or simulator.
React offers a great developer experience for web and mobile development and just recently was added support for desktop (macOS and Windows).
It is important to remember new technologies come with new pains too, but React keeps evolving really fast to be state of the art.