Cross-Language React We are all here because of this library known as React. It’s a library for building User Interfaces. It was introduced to the world as a JavaScript library, and I assume for pretty much everyone here, when you think of “React” you think JavaScript. At first, the only platform React targeted was the web However, over time some folks realized that React’s declarative approach allowed for a clean separation between application’s implementation and the underlying platform’s implementation. By leveraging the same React Library in a JavaScript VM hosted with the app... ...React Native enabled react development for iOS and Android apps. This architecture meant that although we were able to enable the React ecosystem to work with native app development, the engineers that this most appealed to were JavaScript engineers. The end user platforms we were targeting were the web, Android, and iOS, but the developer ecosystems we were targeting was the JavaScript ecosystem. ? ? This means that for a lot of native engineers in these ecosystems, React Native represents not only a di ff erent programming language, but an entirely new UI programming paradigm as well. Android and iOS have both just gotten first class support for two new and exciting programming languages: Kotlin and Swift. Despite being new languages, the UI programming model these ecosystems largely follow is the same and have a lot of the same problems that React was built to solve on the web. So I decided to play around with the concept of building React from the ground up in Kotlin and Swift, without any JavaScript dependency whatsoever. I called this library “Recoil”, and may refer to it as such in this talk, but the name or this particular implementation is not the focus of this talk, and as I mentioned earlier, it is not a production ready implementation. What does it look like? Its worth asking what the public API this looks like. React’s public API surface area is actually quite small. The important bits really come down to the component API and its observable behaviors. First, lets take a look at what this looks like in JavaScript. JavaScript (w/ JSX) class Button extends Component { render() { return ( <View onPress={this.props.onPress} > {this.props.title} </View> ); } } The way most of you probably work with the React component API is something like this. React has a class-based component API which we can subclass, with a required render function where we write this XML-like syntax called JSX. JavaScript (w/ JSX + Flow) type ButtonProps = { title: string, onPress: () => void, } class Button extends Component<ButtonProps, null> { render() { return ( <View onPress={this.props.onPress} > {this.props.title} </View> ); } } And some of you may use React with optional Typings via tools like TypeScript or Flow, where it would look something like this JavaScript (w/ Flow) type ButtonProps = { title: string, onPress: () => void, } class Button extends Component<ButtonProps, null> { render() { return ( h(View, { onPress: this.props.onPress, }, this.props.title ) ); } } If we desugar that JSX syntax, we would get something like this. A JSX element desugars essentially into a function invocation. By convention, here we are calling that function “h”. Swift struct ButtonProps { let title: String let onPress: () -> () } class Button: Component<ButtonProps> { override func render() -> Element? { return ( h(View.self , ViewProps( onPress: props.onPress )) { h(props.title) } ) } } Now this is something that maps rather nicely to Swift. We are able to have a Component base class, with a prop type as a generic argument, and an overridable render method. Inside of render, we create elements with an “h” function, passing in the reference of the components we would like to render, along with props the component expects. Additionally, we can special case children to be returned by a lambda block as a last escaping parameter of h. Kotlin data class ButtonProps( val title: String, val onPress: () -> Unit ) class Button(props: ButtonProps): Component<ButtonProps>(props) { override fun render(): Element? { return ( h(::View, ViewProps( onPress = props.onPress )) { h(props.title) } ) } } The Kotlin version is almost identical. we can express components as a generic class with the prop type as a type argument. One important thing to note is that there is no type here that is platform specific. Everything here is just vanilla swift or vanilla kotlin, and doesn’t need to import any of the platform View APIs. Wait, but why? React is a JS library, why would you want to create it in another language? In order words, why is the React paradigm compelling? What does it have that existing solutions don’t have? I’m going to talk about a couple of things that make React compelling that have absolutely nothing to do with the underlying language or platform. Declarative vs Imperative React is declarative, whereas iOS and Android’s view frameworks are largely imperative. A declarative view framework can drastically simplify the code of our UI. 8 99+ For 0 unread messges, we show just an empty envelope For 0-99 unread messages, we show an envelope with paper and a badge with the count of unread messages For 100 or more unreal messages, we want to be a little bit playful and show “99+” in the badge and an “on fire” icon. Lets consider an example of a small UI. Here we are rendering an inbox icon for a mail or chat application. In this case we have some application state: how many unread messages the user has. function render(count) { if (count > 0 && !hasBadge()) { addBadge(); } else if (count === 0 && hasBadge()) { removeBadge(); } if (count > 99 && !hasFire()) { addFire(); setBadgeText('99+') } else if (count <= 99 && hasFire()) { removeFire(); } if (count > 0 && !hasPaper()) { removePaper(); } else if (count === 0 && hasPaper()) { addPaper(); } if (count <= 99) { setBadgeText(count.toString()) } } To define this UI imperatively, lets say we have an update function. We receive our new state, the updated count, and we have to update the UI. We might implement it something like this. In this case we see that getting to the *next* state very much depends on what our *current* state is. function render(count) { return ( <Envelope fire={count > 99} paper={count > 0}> <Badge visible={count > 0}> {count} </Badge> </Envelope> ); } An identical UI, expressed declaratively, is somewhat simpler. Provided a count, we simple return the state of the UI that is desired for that count. While in the imperative version we had to consider what the previous state of the UI was, in the declarative approach we no longer need to do so, as the underlying framework or runtime is figuring this out for you. In most cases this is all that is desired. Encapsulation Another thing about the React paradigm that I think is important is the encapsulation and well defined ownership it enforces. A component is given props. A component manages state. The public API of a component is clearly defined. A component’s public API is the set of props it accepts. A component is not allowed to change any of the props it is given. Further, a component can have state. State, unlike props, is managed by the component itself, and ONLY that component.