Bindings are nothing but FFI
According to wiki:
A foreign function interface (FFI) is a mechanism by which a program written in one programming language can call routines or make use of services written in another.
ReScript bindings are just an unsafe direct-access mechanism to the JS world. Just some external declarations and some type definitions.
In this post, we are going to write few bindings for the antd library.
Let's get started with a very simple component that doesn't accept any props or children.
Named Import
Component
// closeCircleOutlined.jsx
import { CloseOutlined } from '@ant-design/icons'
/* npm install --save @ant-design/icons */
ReactDOM.render(
<CloseOutlined />,
mountNode,
);
Binding:
// Bindings.res
module CloseCircleOutlined = {
@module("@ant-design/icons") @react.component
external make: React.element = "CloseCircleOutlined"
}
Default Import
// Bindings.res
// import CloseOutlined from '@ant-design/icons'
module CloseOutlined = {
@module("@ant-design/icons") @react.component
external make: React.element = "default"
}
I'm assuming you know what decorators are. If you haven't read about them before then you can read here
The ** basic structure ** is
module ComponentName = {
@module("<node_module_name>") @react.component
external make: React.element = "<NamedImport> or <default>"
}
This component doesn't accept any props yet.
Let's write another binding for a button.
Component:
import { Button } from 'antd';
ReactDOM.render(
<>
<Button shape="circle">Circle Button</Button>
<Button shape="round">Round Button</Button>
</>,
mountNode,
);
Binding:
Copy the structure and fill in the names.
// Bindings.res
module Button = {
@module("antd") @react.component
external make: React.element = "Button"
}
At this point, you can only use the button as
<Button/>
not as
<Button shape="circle">Text</Button>
Props
Let's add a shape
prop.
// Bindings.res
module Button = {
@module("antd") @react.component
external make: (~shape:string) => React.element = "Button"
}
Remember, we have to declare each prop as a Named argument.
Keyword prop
Now, here is a little tricky one. Let's add a type
prop.
// Bindings.res
module Button = {
@module("antd") @react.component
external make: (~\"type": string, ~shape:string) => React.element = "Button"
}
type
is a keyword in a rescript so whenever we use a keyword we have to escape it.
Children
To accept the child component, we will use children
named argument
// Bindings.res
module Button = {
@module("antd") @react.component
external make: (~children:React.element, ~shape:string, ~\"type": string) => React.element = "Button"
}
// App.res
ReactDOM.render(
<Button \"type"="primary" shape="circle">{React.string("Click me")}</Button>,
mountNode
)
React prop
// Bindings.res
module Button = {
@module("antd") @react.component
external make: (~icon: React.element, ~children:React.element, ~shape:string, ~\"type": string) => React.element = "Button"
}
// App.res
ReactDOM.render(
<Button icon={<DownloadOutlined />} shape="circle" \"type"="primary">{React.string("Click me")}</Button>,
mountNode
)
That's all folks!
Top comments (10)
Trying to judge the annotation work in ReScript vs. TypeScript...:
How much work is it to write type bindings for 3rd party libraries that you use in ReScript?
As opposed to writing types for your own data structures in TypeScript?
That seems like the basic tradeoff. If you use a lot of 3rd party libraries, it seems like ReScript would be significantly more annotation work (in the form of bindings). Whereas if you use few 3rd party libraries, or your codebase is at a gigantic scale, then it seems like the annotation work in ReScript would be less than in TypeScript.
What is your experience?
Actually, we have to only write rescript bindings for the functions we use from 3rd party library. I think it's worth it compared to type safety we get from rescript.
Yes, I know. I presumed that when I asked, but I could have been clearer on that.
It seems the tradeoff is:
TS = Write types for your own data structures. But generally be able to use functions from 3rd party libraries without having to writing types/bindings for them (since types are most often generally available via. definitelyTyped etc.).
ReScript = Don't need to annotate types for your own data structures, due to powerful inference. But have to write type bindings for the specific data structures or functions that you use from 3rd party libraries.
How is this tradeoff in your experience? Which results in less boilerplate work in total?
It's more in rescript if you write
.resi
files.I see. And if you don’t?
If you don't write
.resi
then all your function inres
files will be public.Yes. I’m assuming the situation is app dev and not library dev. Then all functions being public is not a problem I presume.
In that case, how much work is it in ReScript (compared to TS)?
Then it would be less work in Rescript. However, I can't quantify it.
Thanks!
thanks for sharing