DEV Community

Arbaaz
Arbaaz

Posted on • Updated on • Originally published at arbaaz.io

How to write rescript bindings for a react library

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,
);
Enter fullscreen mode Exit fullscreen mode

Binding:

// Bindings.res
module CloseCircleOutlined = {
 @module("@ant-design/icons") @react.component
 external make: React.element = "CloseCircleOutlined"
}

Enter fullscreen mode Exit fullscreen mode

Default Import

// Bindings.res

// import CloseOutlined from '@ant-design/icons'
module CloseOutlined = {
 @module("@ant-design/icons") @react.component
 external make: React.element = "default"
}

Enter fullscreen mode Exit fullscreen mode

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>"
}

Enter fullscreen mode Exit fullscreen mode

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,
);
Enter fullscreen mode Exit fullscreen mode

Binding:

Copy the structure and fill in the names.

// Bindings.res
module Button = {
 @module("antd") @react.component
 external make: React.element = "Button"
}

Enter fullscreen mode Exit fullscreen mode

At this point, you can only use the button as

<Button/>
Enter fullscreen mode Exit fullscreen mode

not as

<Button shape="circle">Text</Button>
Enter fullscreen mode Exit fullscreen mode

Props

Let's add a shape prop.

// Bindings.res
module Button = {
 @module("antd") @react.component
 external make: (~shape:string) => React.element = "Button"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode
// App.res
ReactDOM.render(
 <Button \"type"="primary" shape="circle">{React.string("Click me")}</Button>, 
 mountNode
)
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode
// App.res
ReactDOM.render(
 <Button icon={<DownloadOutlined />} shape="circle" \"type"="primary">{React.string("Click me")}</Button>, 
 mountNode
)
Enter fullscreen mode Exit fullscreen mode

That's all folks!

Latest comments (10)

Collapse
 
rizkimcitra profile image
R. Maulana Citra

thanks for sharing

Collapse
 
redbar0n profile image
Magne

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?

Collapse
 
arbaaz profile image
Arbaaz

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.

Collapse
 
redbar0n profile image
Magne

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?

Thread Thread
 
arbaaz profile image
Arbaaz

It's more in rescript if you write .resi files.

Thread Thread
 
redbar0n profile image
Magne • Edited

I see. And if you don’t?

Thread Thread
 
arbaaz profile image
Arbaaz

If you don't write .resi then all your function in res files will be public.

Thread Thread
 
redbar0n profile image
Magne • Edited

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)?

Thread Thread
 
arbaaz profile image
Arbaaz

Then it would be less work in Rescript. However, I can't quantify it.

Thread Thread
 
redbar0n profile image
Magne

Thanks!