Skip to content

[Proposal] Allow StyleSheets to be unloaded #15899

@dantman

Description

@dantman

Motivation:
StyleSheet.create is a recommended optimization, it prevents large numbers of serialized objects from being continually passed over the js/native bridge for style updates creating a performance bottleneck. However it is only accessible to 100% static styles; if you work with anything somewhat dynamic (even if those styles will be unchanging for a long period of time and would still benefit significantly by the optimization) you cannot use StyleSheet.create because styles created this way are not unloaded and as values change and you replace styles you'd slowly leak memory (in js and presumably in native as well) as unused style objects stick around.

This is a problem for libraries with some type of theme instance passed down/provided as props/context which "may" change but largely stay the same. Styles are contextual (one area of an app may have a dark theme and the other a light theme, or there may be more complex differences) and so any styles affected by the theme cannot be defined statically in a StyleSheet.create call outside the component. Which means a number of "mostly" static styles are relegated to being treated as dynamic and getting passed serialized over the js/native bridge on every render.

In most of these situations some sort of provider component and/or HOC is used, in a way that it would be known when a style is no longer being used (because something about the theme has changed or the provider has been unmounted). All these components need, is a way to unload a style registered with StyleSheet.create.

Relevant code and issues:
StyleSheet registration is done through ReactNativePropRegistry. The source does not appear to be available within the react-native but inside Libraries/Renderer/ReactNative{Stack,Fiber}-{dev,prod}.js you can see the compiled code. It's a relatively simple module that just has an objects variable; a register function that increments a unique id, freezes the object, and sets it on the object; and a getByID that accepts ids and returns the objects stored in objects.

If the source were accessible, setting values on objects to null as part of unloading would be simple.

However the whole point of StyleSheet creation is that objects are not continually passed along the js/native bridge, so presumably there is a cache on native side for style objects that also needs to be unloaded in order to prevent memory leaking. And I presume native code doesn't bother calling getByID all the time when it already has an object so we can't rely on unloading that way, presumably the simplest method would be for ReactNativePropRegistry to have a blacklist of ids that have been unloaded which can be passed along the bridge next time some style info is passed along the bridge (the blacklist can be emptied after native is done with it to avoid it growing indefinitely).

Unfortunately I do not know how these style objects registered in ReactNativePropRegistry get across the bridge to native code. Trying various acks and skimming the Android code I haven't been able to find the code responsible for passing styles along the bridge that would presumably be caching it. Wherever this is it feels like it's buried so deep in react-native it's inaccessible to community contributors.

Real world use case:
I'm working on a library with react-native components that follow Material Design. Material Design has configurable style attributes (primary and accent color, a light and dark theme, component specific customizations, etc...) and these can be contextual (an app may have a light theme and text may be dark, but text may switch to light in the context of a solid coloured app bar; different areas of the app may have different primary colors or appBar colors; ...). So I have a theme instance that is passed down using a MaterialThemeProvider component, much like react-redux's <Provider> or Material-UI's <MuiThemeProvider>. The provided theme is used by my library's components to determine their styles and in some cases used by consumers of the library for their styles.

Because these style attributes are "provided" all the styles that are based on them (background colors, text colors, possibly padding and height, or more) end up being treated as dynamic and passed in style objects to style. If it were possible to unload styles I would be able to provide a decorator/HOC that could be used by components (internal or part of consuming apps) to define styles using the MaterialTheme instance that the HOC along with the MaterialThemeProvider would register with StyleSheet.create and unload when the provider is unloaded or the material theme changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Ran CommandsOne of our bots successfully processed a command.Resolution: LockedThis issue was locked by the bot.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions