Skip to content

Files

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Latest commit

9c133cb · Jan 3, 2025

History

History
119 lines (87 loc) · 3.19 KB

mutation.mdx

File metadata and controls

119 lines (87 loc) · 3.19 KB
title description canonical
Mutation
Imperative and mutative programming capabilities in ReScript
/docs/manual/v11.0.0/mutation

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.

Mutate Let-binding

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

Usage

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 refs allocations are optimized away.

Tip & Tricks

Before reaching for ref, know that you can achieve lightweight, local "mutations" through overriding let bindings.

Self-Referencing Assignments

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();
                  })
              })
        }));