There seems to always be vagueness to the understanding of styles in react native, as things quickly develop and change, it’s easy to lose track of these things. React Native is still exploring a lot of aspects and making important changes on how everything works.
TL; DR
- Don’t use inline styles on pure or memoized components.
- Just because you can use inline styles, doesn’t mean that you shouldn’t keep your code clean, code quality is important.
- Inline styles no longer have a formidable effect on performance like before, unless you go crazy with it.
Back to the Future
If you go back to the React Native v0.55.4
and then look at the create function of the StyleSheet, then you would see something like this:
create<+S: ____Styles_Internal>(
obj: S,
): $ObjMap<S, (Object) => StyleSheetInternalStyleIdentifier> {
const result = {};
for (const key in obj) {
StyleSheetValidation.validateStyle(key, obj);
result[key] = obj[key] && ReactNativePropRegistry.register(obj[key]);
}
return result;
},
ReactNativePropRegistry
Skimming through the above code block, you’ll see that it’s not a function that simply returns the stye object back to us. There is a specific call to ReactNativePropRegistry.register
. Now this ReactNativePropRegistry
is interesting, if we go to it’s definition, we will find that the definition has the two functions inside, register
and getByID
:
ReactNativePropRegistry = (function() {
function ReactNativePropRegistry() {
if (!(this instanceof ReactNativePropRegistry))
throw new TypeError("Cannot call a class as a function");
}
ReactNativePropRegistry.register = function(object) {
var id = ++uniqueID;
objects[id] = object;
return id;
};
ReactNativePropRegistry.getByID = function(id) {
if (!id) return emptyObject$2;
var object = objects[id];
return object
? object
: (console.warn("Invalid style with id `" + id + "`. Skipping ..."),
emptyObject$2);
};
return ReactNativePropRegistry;
})(),
Let’s take a look at what’s going on here. If you recall, the previous code block also used the register function. With this code, every object that’s being passed into register, hence being registered as a style object, will be attached with a uniqueID
. Through this uniqueID
every object that’s registered will be able to be distinguished.
Now why is this important? This means that the styles are actually not kept in the component and are completely referenced from outside, which brings us to getByID
. This basically lets React Native get the style object that it requires. You can see an example usage here.
0.57 Docs
The version 0.57 documentation of the StyleSheet is the final version before we are going to see the update that’s coming (which I’m also going to mention below). In this documentation we can clearly see that the performance is mentioned as an important aspect of StyleSheet.
Performance:
- Making a stylesheet from a style object makes it possible to refer to it by ID instead of creating a new style object every time.
- It also allows to send the style only once through the bridge. All subsequent uses are going to refer an id (not implemented yet).
Considering everything we went through, it would make a lot of sense to strictly stick to using StyleSheet
. But right after the v0.55.4, things changed a bit for styles in React Native.
Roads? Where We’re Going, We Don’t Need Roads
Let’s come back to today Marty, where we definitely need roads and where the documentation, the creation and the usage of StyleSheet
has changed.
A Simple JS Object
The whole concept of saving style objects by id and holding them in a specific place like ReactNativePropRegistry
is not completely gone. Instead we have this:
create<+S: ____Styles_Internal>(obj: S): $ReadOnly<S> {
// TODO: This should return S as the return type. But first,
// we need to codemod all the callsites that are typing this
// return value as a number (even though it was opaque).
if (__DEV__) {
for (const key in obj) {
StyleSheetValidation.validateStyle(key, obj);
if (obj[key]) {
Object.freeze(obj[key]);
}
}
}
return obj;
},
Now in this code block if we look outside of the if (__DEV__)
condition, which would actually give us the production code would be surprisingly that nothing is happening here. We pass an object into create
and that object comes back as it is. This means that it’s not that different from actually not using the create function (keep using it for other reasons like validation), but performance-wise it’s actually now no different than a regular JS object.
Latest Docs
If you go into the latest documentation on React Native website, you’d actually no longer be able to see the part about performance, instead you’d only see tips about code quality.
Code quality tips:
- By moving styles away from the render function, you’re making the code easier to understand.
- Naming the styles is a good way to add meaning to the low-level components in the render function.
Make no mistake, code quality is very important. Now, this is usually something to decide with your team, but separating long styles into the StyleSheet and always keeping your return
as clean as possible is good for the eyes and hearts.
Recreated objects
Don’t forget, if you use inline styles the style object you added will be recreated on every render, meanwhile this is not happening with StyleSheet
as it is outside of the render. This wouldn’t have a big effect as one little object doesn’t cause trouble but beware on very big projects with very big inline styles.
Re-render
I have heard many misconceptions about inline styles causing re-render all the time and this is wrong. It’s clearly stated in many blogs and posts that inline styles cause re-render for Pure and memoized components. They have no effect on regular components as regular components get re-rendered no matter what if they or their parents change props or state. I will dive into this more in my other post, but I felt this should be mentioned here as well.