React to Jetpack Compose Dictionary
Two libraries with similar concepts. What's the equivalent Jetpack Compose terms for React terms?
I’ve been trying out Jetpack Compose on a personal project and liking the API. Compose is a pretty big API shift, and I’ve found my React knowledge much more helpful than my Android knowledge. Perhaps this is how React Native developers come to replace native Android developers.
Many concepts and functions in the two libraries work the same but have different names. Here’s a compilation of terms I’ve seen along with explanations. This list is up to date for Jetpack Compose, and based on version 1.1.1.
Children Prop > Children Composable
Both React and Compose refer to elements to be displayed inside another UI component as children.
React passes children by value, under a special prop named children
.
Jetpack Compose passes composable functions as the functions themselves don’t return anything.
Context > CompositionLocal
While most data is passed through the component tree as props/parameters, sometimes this model can be cumbersome. React includes the Context API to share this data. Compose uses CompositionLocal to accomplish the same thing. This API was called Ambient
in alpha versions of Jetpack Compose.
createContext > compositionLocalOf
A React Context is created with React.createContext
, while a Jetpack Compose ambient is created with compositionLocalOf
.
Provider > CompositionLocalProvider
The value can be controlled using a “Provider” in both React and Compose.
useContext > CompositionLocal.current
Accessing the React context value is accomplished using the useContext
hook.
Accessing the value of an Ambient is done by using the .current
getter.
Hook > Effect
React lets you build your own hooks to extract component logic into reusable functions. They can use other hooks like useState
and useEffect
to encapsulate logic that relates to the component lifecycle.
In Jetpack Compose, @Composable functions are used as the equivalent of hooks (along with acting as the equivalent of Components). These composable functions, sometimes referred to as “effect” functions, usually start with a lowercase letter instead of an uppercase letter.
useEffect > LaunchedEffect
Mutations, subscriptions, timers, logging, and other side effects are not allowed inside the main body of a UI component. They must be placed inside a callback function that both React and Jetpack Compose will call at the correct point.
React has the useEffect
hook to run side effects.
useEffect
is used for many different purposes: event subscriptions, logging, asynchronous code, and more. Compose breaks up these use cases into separate functions with the suffix Effect
, including DisposableEffect
, LaunchedEffect
, and SideEffect
.
Clean-up function > DisposableEffect
Side effects often create resources that need to be cleaned up once the UI component is unused. React allows the useEffect
function to return a second function, known as the clean-up function.
Jetpack Compose exposes an DisposableEffect
composable that replaces useEffect
in this scenario. Rather than returning a function, you can call onDispose
and pass a callback there. It will run when the effect leaves the composition.
useEffect(promise, deps) > LaunchedEffect
Asynchronous functions are created in JavaScript using the async
keyword. React will handle running the function, but doesn’t handle cancelling the promise if a re-render occurs before the promise has finished running. Since you shouldn’t return anything except for a clean-up function from the effect callback, you need to create then immediately invoke the asynchronous function.
The above code fetches from an API whenever the id
value changes. You can also write the same code using an IIFE or Immediately Invoked Function Expression.
Cancelling the promise is a little more complicated because it isn’t built into React. You can use an AbortController
to cancel fetch
functions and certain other promises.
Kotlin uses suspend functions and coroutines instead of JavaScript’s async functions and promises. Jetpack Compose has a dedicated LaunchedEffect
composable to handle suspend functions in side effects. The effect dependencies are called “keys” and work the same way as React, expect that you provide them at the start of the function instead of the end.
Compose additionally cancels the coroutine whenever the keys change. As a result, the 11 lines of code above are replaced with the 3 lines below.
useEffect(callback) > SideEffect(callback)
Side effects that run every single render are created in React using useEffect
with no dependency list.
This is accomplished using the SideEffect
function in Jetpack Compose. These side effects will be run every single composition.
Dependency array > Keys
useEffect
takes an optional second parameter: an array containing the values the effect depends on. If this parameter isn’t passed at all, then the effect will run every single render. If an empty array is passed in, then the effect is only run when the component is first rendered.
Rather than passing an array as the last parameter, you can pass any number of arguments as the first few parameters to DisposableEffect
and LaunchedEffect
.
If you want to run something every render, you need to use SideEffect
. There is no equivalent for not passing the dependency list to useEffect
in DisposableEffect
and LaunchedEffect
. You should always pass some kind of dependency or key.
To only run an effect on the first composition, you should use a key that never changes - such as Unit
or true
.
useState with useEffect > produceState
In React, frequently you will use a combination of the useEffect
hook to fetch data, and useState
to store that data.
Jetpack Compose offers a single function to handle this functionality: produceState
.
Instead of calling a set function, you can use the value
setter inside of the produce state scope. You can read the current state by looking at the return value from produceState
.
The side effect inside of produceState
can be a coroutine or choose to clean itself up using the awaitDispose
function. This is similar to returning a clean-up function inside React’s useEffect
hook.
Key Prop > Key Composable
Keys are used to help React and Jetpack Compose identify which items have changed, are added, or are removed. They must be unique among the list of items you display at that point in a UI component.
React has a special string prop named key
.
Jetpack Compose has a special utility composable called key
that can take any input.
Multiple inputs can be passed in to key
in Compose, while React requires a single string (although multiple strings could be concatenated).
.map > For Loop
Since React passes elements by value, elements corresponding to an array are usually created using array.map()
. The returned elements in the map callback can be embedded in JSX.
Composable UI functions in Jetpack Compose emit other UI composables and don’t return anything. As a result, a simple for loop can be used instead of .map()
.
In fact, any iteration method can be used, such as .forEach()
.
memo > skippable
In React, a component can be marked as “pure” by wrapping it with React.memo
. This will prevent the component from re-rendering unless its props change.
In Jetpack Compose, this is done automatically. Compose will automatically try to mark a function as “skippable”, which will prevent the composable from re-composing. If all the parameters of a composable are stable, then the compiler will mark the composable as skippable.
Primitive data types and classes marked with @Stable
or @Immutable
are stable. Collection classes list List
need to be wrapped with by a class or replaced with KotlinX immutable collections.
PropTypes.oneOfType > Function overloading
JavaScript and TypeScript support passing in different variable types for the same parameter. In React, this can be modelled using PropTypes.oneOfType
. In TypeScript, it can be modelled using a union type.
This is possible to model using overloads in Kotlin.
useMemo > remember
React allows values to be re-computed only if certain dependencies change inside a component through the useMemo
hook.
Jetpack Compose has a similar function named remember
that only re-computes a value if the inputs change.
React Component > Composable
In React, Components are used to split the UI into independent and reusable pieces. They can come in the form of a function that takes a props
parameter and returns a React node.
In Jetpack Compose, Composable functions are building blocks used to split the UI into independent and reusable pieces. They are functions with a @Composable
annotation that can take any number of parameters.
Both libraries also refer to these concepts as UI components. However, Jetpack Compose also uses composable functions for other functionality, see hook > effect and key for examples.
Render > Composition
Once data has changed inside a UI component, the library must adjust what is presented on screen. React refers to this as rendering, while Jetpack Compose refers to this as composition.
Reconciler > Composer
Internally React needs to figure out what changes when a component is rendered. This diffing algorithm is called the “Reconciler”. React Fiber referred to the release of the new Fiber Reconciler which replaced the old algorithm.
Jetpack Compose’s diffing is done using the Composer. It determines how nodes change every time a composable completes composition.
State > State
Both React and Compose refer to local variables you want to mutate as “state”.
useState > state
Creating a new state variable in React is done with the useState
hook. It returns a tuple with the value and a setter.
Compose uses the mutableStateOf
function to return a MutableState
object, which contains a variable with a getter and setter.
MutableState
contains componentN()
functions, allowing you to destructure the getter and setter just like React.
To avoid recomputing the initial state, mutableStateOf
is usually wrapped with the remember
function.
setState updater function > Snapshot
When updating state in a React component based on a previous value, you can pass an “updater” function that receives the current state as a function argument.
In Jetpack Compose, this behaviour is encapsulated in a concept called snapshots. A snapshot represents state values at a certain time. The Snapshot
class exposes an enter
function to run an updater callback. Inside the callback, all state objects return the value they had when the snapshot was taken.
Storybook > Preview
The Storybook tool helps you preview React components on the web independently by creating “stories”. The @Preview
annotation in Jetpack Compose lets you build example composables that can be previewed in Android Studio.
Ternary Operator > If Statement
React components often use the ternary operator (cond ? a : b
) to conditionally render components, as the successful branch is returned (unlike if statements in JavaScript).
Kotlin doesn’t have ternary operators as if statements do return the result of the successful branch. Since if statements in Kotlin act like ternary operators in JavaScript, there is no need for a second variation.