What I wish I knew about React
A couple weeks ago I started working on my first React application. Not only was it my first React application, but it was also my first React Native application, so a lot was new to me all in one go.
Coming from the Angular (and Ionic) world, it felt like all my preconceived notions and ideas were being turned upside down. I managed to put together (what I think is) a pretty decent application, but I had a lot of roadblocks along the way. This is partly because I prefer to learn by just diving in (the only tutorial I did was this React Native Crash Course). So, a lot of these roadblocks could have been solved by me just reading the documentation.
That said, in case there’s anyone else there like me, here are a few things I wish I knew before I got started.
React is a library, not a framework #
This is something I technically knew as I had heard it several times before, but I didn’t really internalise it. I sort of assumed this was more of a technicality, rather than a key piece of information around which I should redefine my expectations.
What’s the difference between a library and a framework? #
The difference between a library and a framework is, it turns out, very key. Let’s use an analogy of trying to build a house. With a framework, you’re essentially given all the materials to build that house, and it’s up to you in what arrangement you put them in. You will very rarely need to step outside the materials you have been given to use something third-party and, in some cases, you may not be allowed to step outside the materials.
With a library, on the other hand, you start with essentially nothing. The library’s materials are a limited set in comparison to a framework’s materials, and you can pick/choose when you want to use them and when you want to step outside that and use third-party materials.
What does that really mean? #
For me, this understanding allowed me to step away from thinking that a React-specific solution was what I needed for everything.
For example, I spent hours trying to figure out how to do simple things like making a request to an API in React. In Angular, I would use their Angular-specific HttpClient
to make such requests, but that doesn’t exist in React. It wasn’t until a kind twitter user made the (now obvious) point that I could just use a simple Javascript fetch()
request, that I had this moment of realisation.
So now, I’ve redefined my expectations of what React will do for me. When I have a problem to solve, I know to think Javascript-first instead of React-first.
React is a UI library #
Not only is React a library, it’s specifically for User Interfaces. Again, this should be obvious considering it says so on their website, but I didn’t internalise it enough. The reason this is so important to understand is similar to why it should have been obvious to me that I could just use the browser’s native fetch()
API.
If what you’re trying to do isn’t directly related to something visual on screen, React does not care
The way I like to think of it in comparison to Angular is this: Angular is a framework for architecting and building applications. It, for the most part, doesn’t change the way you write your HTML or CSS, but it heavily controls the way you write Javascript.
React, on the other hand, is a library for building user interfaces. It, for the most part, doesn’t change the way you write Javascript that isn’t directly related to your UI, but it heavily controls the way you write HTML and CSS (or the equivalent of it). And this brings me to the next point…
React component libraries are always for UI #
This is something that really made no sense to me the first time I realised it. Take, for example, the apollo-react
library. In that library, you can use a <Query>
element, which will perform a GraphQL query and return the result, right in your component markup.
import React from ‘react’;
import {Text} from ‘react-native’;
import gql from ‘graphql-tag’;
import {Query} from ‘react-apollo’;
const MY_QUERY = gql`query …{} `;
const ExamplePage = (props) => {
return (
<View>
<Query query={MY_QUERY}>
{({ loading, error, data }) => {
if (loading) return <Text>Loading</Text>;
if (error) return <Text>Error</Text>;
return <Text>{ JSON.stringify(data) }</Text>;
}}
</Query>
</View>
);
};
Being so used to the model-view-controller way of architecting applications, this seemed (and, to be honest, still seems) like such an unnatural way to interface with an API. Granted, you can also use the useQuery()
Javascript function from the react-apollo
library, but it has its own pretty significant limitations too.
I also came across this issue when trying to use ActionCable with React. The react-actioncable-provider
package is for subscribing to ActionCable channels in your UI component. If you just want to subscribe to a channel and not immediately display data on the page, the package isn’t what you need.
Components will update, whether you want them to or not #
Before I understood why and when components updated, it seemed like my entire application was stuck in an infinite loop. I would do something like make an API call right at the root of the component, which itself would update the state, causing the component to re-render, and the API call would be made again.
const BadExamplePage = (props) => {
const [user, setUser] = useState();
getUser().then((u) => setUser(u));
return (
…
);
};
If you use React, you’ll understand why this is a terrible idea. The getUser()
function is called when the component updates. It then changes the state of the user
variable, and this change causes the component to update, and so on and so on.
What I have since learned is that we can't always control exactly when or how often the component will re-render so, for use cases like this, we need useEffect()
…
useEffect is a major 🔑 #
useEffect()
is like a safe haven to me in the confusing world of React components. It’s for those cases where you’re just like, “Can I just write Javascript that will execute once please?”.
Although by default, the code within a useEffect()
block runs on each re-render, we can choose to execute them only when certain values have changed or just once. We do this by passing a second argument to useEffect()
, with an array of variables that, if their value should change, we want the function to be run again.
const BetterExamplePage = (props) => {
const [user, setUser] = useState();
useEffect(() => {
getUser().then((u) => setUser(u));
}, [ /* dependencies */ ]);
return (
…
);
};
If we leave the array empty, that effectively means that the function will only run once.
I’m still on this React journey so I'm sure there’s still a lot for me to learn, but these are the things that I've found challenging so far.