title | description | canonical |
---|---|---|
Mutation |
Imperative and mutative programming capabilities in ReScript |
/docs/manual/v11.0.0/mutation |
ReScript has great traditional imperative & mutative programming capabilities. You should use these features sparingly, but sometimes they allow your code to be more performant and written in a more familiar pattern.
Let-bindings are immutable, but you can wrap it with a ref
, exposed as a record with a single mutable field in the standard library:
<CodeTab labels={["ReScript", "JS Output"]}>
let myValue = ref(5)
var myValue = {
contents: 5
};
You can get the actual value of a ref
box through accessing its contents
field:
<CodeTab labels={["ReScript", "JS Output"]}>
let five = myValue.contents // 5
var five = myValue.contents;
Assign a new value to myValue
like so:
<CodeTab labels={["ReScript", "JS Output"]}>
myValue.contents = 6
myValue.contents = 6;
We provide a syntax sugar for this:
<CodeTab labels={["ReScript", "JS Output"]}>
myValue := 6
myValue.contents = 6;
Note that the previous binding five
stays 5
, since it got the underlying item on the ref
box, not the ref
itself.
Note: you might see in the JS output tabs above that ref
allocates an object. Worry not; local, non-exported ref
s allocations are optimized away.
Before reaching for ref
, know that you can achieve lightweight, local "mutations" through overriding let bindings.
There are scenarios where using a mutable ref
binding becomes necessary, particularly when dealing with self-referencing. This is common when working with functions that return a value and accept a callback that uses that return value. In such cases, you need to initialize a mutable ref
binding and then assign to it. Here's an example:
<CodeTab labels={["ReScript", "JS Output"]}>
// hypothetic "showDialog" function shows the given `~content: JSX.element`
// in the dialog. It returns a cleanup function that closes/removes the dialog.
@val external showDialog: (~content: Jsx.element) => unit => unit = "showDialog"
// We want render a "Close" button in the `~content` that will call the
// returned cleanup function when clicked. This requires a mutable binding.
// First initialize it with a dummy function that has the same signature as
// our cleanup function.
let cleanupDialog = ref(() => ())
// assign to the ref and self-refer to the value in the event handler
cleanupDialog :=
showDialog(
~content=<div>
<button onClick={_ => cleanupDialog.contents()}>
{React.string("Close")}
</button>
</div>,
)
var cleanupDialog = {
contents: (function () {})
};
cleanupDialog.contents = showDialog(JsxRuntime.jsx("div", {
children: JsxRuntime.jsx("button", {
children: "Close",
onClick: (function (param) {
cleanupDialog.contents();
})
})
}));