Supporting Upcoming Features in React Canary for ReScript

copied from: Support react@canary release · Issue #101 · rescript-lang/rescript-react · GitHub


Hello everyone,

The React team plans to introduce new hooks, APIs, and element/node types in the upcoming React Canary release. I have summarized some of the changes that I believe would be useful for us to support in ReScript. Your input and feedback would be highly appreciated.

New React hooks and APIs include:

  • use: (promise<'value> | context<'value>) => 'value
    • I could introduce a new type valueContainer<'value> to express this, as defined in the RFC.
  • cache: 'value => 'value
    • TypeScript: export function cache<CachedFunction extends Function>(fn: CachedFunction): CachedFunction
  • startTransition: ((. unit => unit) => unit)

Some of the new ReactDOM hooks and APIs are:

  • flushSync: ((. unit => unit) => unit)
  • experimental_useFormStatus: (unit => { pending: boolean, action: promise<void> | (data: FormData) => unit, method: string, data: FormData })
  • experimental_useOptimistic: (@uncurry ('state, ('state, 'action) => 'state)) => ('state, ('action) => unit)

There’s also support for Client Action Functions: string | (formData: FormData) => unit

  • <button formAction={(formData) => { ... }} />
  • <form action={(formData) => { ... }} />

I’m also not certain if @rescript/react currently supports FormData. It might make more sense to refer to the rescript-webapi library, which provides ReScript bindings for Web APIs.

Also, please bear in mind that some of the type expressions shared in the original post are based on my best efforts to translate from TypeScript, so they may not be perfectly accurate. If anyone has a more precise understanding or can correct any errors, your contributions would be very welcome!

Async components can be written as follows:

ReScript input:

type user = {id: string, name: string}
let fetchUserById = async (userId) => {
  {
    id: userId,
    name: Js.Float.toStringWithRadix(Js.Math.random(), ~radix=16),
  }
}

@react.component
let make = async (~userId) => {
  let user = await fetchUserById(userId)
  <div> {React.string(user.name)} </div>
}

Expected JavaScript output:

import * as JsxRuntime from "react/jsx-runtime";
async function fetchUserById(userId) {
  return { id: userId, name: Math.random().toString(16) };
}
async function Playground(props) {
  var user = await fetchUserById(props.userId);
  return JsxRuntime.jsx("div", { children: user.name });
}
var make = Playground;
export { fetchUserById, make };

Lastly, React Canary introduces new valid nodes such as the Promise node:

ReScript:

type user = {id: string, name: string}
let fetchUserById = async (userId) => {
  {
    id: userId,
    name: Js.Float.toStringWithRadix(Js.Math.random(), ~radix=16),
  }
}

let fetchUserNameById = async (userId) => {
  let user = await fetchUserById(userId)
  user.name
}

@react.component
let make = (~userId) => {
  let userName = fetchUserNameById(userId)
  <React.Suspense>
    {React.promise(userName)}
  </React.Suspense>
}

Expected JavaScript output:

import * as React from "react";
import * as JsxRuntime from "react/jsx-runtime";
async function fetchUserById(userId) {
  return { id: userId, name: Math.random().toString(16) };
}
async function fetchUserNameById(userId) {
  return (await fetchUserById(userId)).name;
}
function Playground(props) {
  var userName = fetchUserNameById(props.userId);
  return JsxRuntime.jsx(React.Suspense, { children: userName });
}
var make = Playground;
export { fetchUserById, fetchUserNameById, make };

React.Context nodes have also been introduced.


Thank you for reading. Due to forum policies, I removed the original reference and docs. If you would like to access the original references, please check the original GitHub issue.

5 Likes