A SwiftUI-inspired syntax for React components.
SwiftUI-React
function SwiftUITodos(props) {
Div(() => {
H1("Todos");
Ul(props.todos, todo => {
Li(todo.text).lineThrough(todo.checked);
});
});
}
An alternative approach for representing React components and component hierarchies using function calls instead of JSX, inspired by the syntax used by Apple's SwiftUI.
A port of, or attempt to recreate, SwiftUI or its API within React.
The SwiftUI-React API consists of one factory function, SwiftUI()
, and the components it creates. Swift-React components can be used as React components or called as regular functions. When called as functions, they will add React components to the React DOM just as though they were used as React components.
A factory function for creating SwiftUI-React components.
function SwiftUI(
executor:
ReactElement |
ReactComponent |
((value: any, props: object) => ReactElement | void)
):SwiftUIComponent
executor: A React element, React component (function or class), or specialized component function that includes a value
parameter along with the standard function component props
parameter that is to be converted into a SwiftUI-React component or element instance.
A unique SwiftUIComponent function representing the component created, or a React element thats an instance of a SwiftUIComponent wrapping the React element passed.
Essentially, SwiftUI()
converts React components into SwiftUI-React components. It supports both function components and class components as well as a third variation of a function component that has two parameters.
const SwiftUIComponent = SwiftUI(ReactComponent);
The third function variation - a value-first component - is recognized when the arity of a function given to SwiftUI()
is greater than 1. In this case, when the component is called, it would expect the first argument to be its value
and the second to be its props
rather than the first being the props
as is the normal behavior. The value
argument is simply a convenience parameter that maps to props.componentValue
.
const SuiComp = SwiftUI(function Comp (value, props) {});
SuiComp(100);
// is the same as
const SuiComp = SwiftUI(function Comp (props) {});
SuiComp({ componentValue: 100 });
Aside from components, it can also convert React elements into SwiftUI-React elements. This is not normally necessary because you would generally just create and use SwiftUI-React components.
function Comp (props) {}
SwiftUI(<Comp />);
// is the same as
function Comp (props) {}
const SuiComp = SwiftUI(Comp);
<SuiComp />;
Components returned from SwiftUI()
calls are callable class constructors that can be used as React components or called as normal functions. These constructors are identified in this documentation as having the type SwiftUIComponent
but each instance of SwiftUIComponent
returned from SwiftUI()
is unique.
A component type created by calling SwiftUI()
in the form of a callable constructor function.
type ChildrenExecutor =
((value?: any) => ReactElement | void) |
((value: any, index: number, collection: any[]) => ReactElement | void)
function SwiftUIComponent(
value?: any,
props?: object,
childrenExecutor?: ChildrenExecutor
):Proxy;
function SwiftUIComponent(
value: any,
childrenExecutor: ChildrenExecutor
):Proxy;
function SwiftUIComponent(
props: object,
childrenExecutor: ChildrenExecutor
):Proxy;
function SwiftUIComponent(
props: object
):Proxy;
function SwiftUIComponent(
childrenExecutor: ChildrenExecutor
):Proxy;
value: The value pased into the function call, or whats in props.componentValue
. This is used if the component was defined as a value-first component or if the component was called with a non-object first argument.
props: The props object to use for the component.
childrenExecutor: A function that is used to set up the children of the component. This will either get passed props.componentValue
or if props.componentValue
is an array, will be treated as a map function that maps over all the values in that array.
A Proxy object which transforms method calls into props setters, where the name of the method called becomes a prop name and the argument(s) passed in become its value.
SwiftUI-React components can be used as React components or called as functions in other SwiftUI-React components.
When a SwiftUI-React component is called as a function, if the last argument it was given is a function, that function is used to define the children of the component. It will get called with either 0 arguments, 1 argument if a non-array component value is defined and available, or 3 arguments in the form of valueElement, index, valueArray
if value is defined as an array.
Values and props passed into a SwiftUI-React component get sent into their original component definition's render call. Props will also get the following values:
componentValue
: Added if a value is defined for, or otherwise forcibly given to a component call.childrenAll
: An array of all children sets for components that have array values and create children iterating over that array. If a component doesn't have children, this is an empty array. If a component has a child or children but does not loop over children, this will be an array with one element:props.children
.
In order for SwiftUI-React component function calls to be recognized as components, they must be used within the context of another SwiftUI-React component. This means being within the render body of a component that is itself, or has a parent that is a SwiftUI-React component that been added to the React DOM as a normal React component.
const H1 = SwiftUI((value, props) => {
return <h1>{value}</h1>;
});
const App = SwiftUI(function () {
H1("App"); // allowed because App is added as <App /> (below)
});
ReactDOM.render(<App />, document.getElementById("app"));
If your root is not a SwiftUI-React component, you can conveniently wrap it in a call to SwiftUI()
to enable SwiftUI-React component calls within that root component.
function App() {
H1("App");
}
ReactDOM.render(SwiftUI(<App />), document.getElementById("app"));
This project was an exploration in seeing how close a React component could get to using a syntax similar to that seen with SwiftUI. The driving example was the one used on Apple's landing page for SwiftUI.
This example was simple yet demonstrated a number of different characteristics of the SwiftUI that would have made a (React-backed) JavaScript implementation interesting to attempt. The SwiftUI version of this example (called "namerizer") is available in the Examples. Here is the SwiftUI-React version of the code seen in the SwiftUI screenshot:
const Content = View(() => {
const model = Themes.listModel;
List(model.items, { action: model.selectItem }, item => {
Image(item.image);
VStack({ alignment: leading }, () => {
Text(item.title);
Text(item.subtitle)
.color(gray);
});
});
});
Probably not. This was an exploration made without any intention for the code to be used in production with real-world projects. Because it is a wrapper over React's own component implementation, there would also be performance implications with the extra overhead necessary to implement this API.
However, if this approach really floats your boat and you stick to using it in small, personal projects, you should be fine. While it was simply an exploration around syntax, attention was given in attempting to make the solution a robust one.
At first glance, the use of SwfitUI-React components do look very similar to uses of React.createElement
:
React
React.createElement("div", null, [
React.createElement("h1", null, "Todos")
React.createElement("ul", null, todos.map(todo =>
React.createElement("li", {lineThrough: todo.checked}, todo.text)
))
])
SwiftUI-React
Div(() => {
H1("Todos");
Ul(todos, todo => {
Li(todo.text).lineThrough(todo.checked);
});
});
The difference is that SwiftUI components are compositions of functions rather than compositions of objects. This is most apparent with the Div
component as you can see that it uses a function to define its children rather than an array of objects as is seen with the createElement
example.
Each SwiftUI component itself is also a function. While React components can also be functions, or are classes that have render()
methods that serve the same purpose, SwiftUI component functions get called before their respective render calls are made by React, used to create the elements that React will ultimately use in the rendering process.