リファイ:FRIの基本反応


外国語インタフェース(FFI)は、ある言語で書かれたプログラムを別の言語で書かれたプログラムで話す方法です.Rescriptでは、JavaScriptにFFIバインディングを作成しています.我々は、コンセプトに触れたが、このポストでは、我々はいくつかの最も一般的な結合のいくつかを我々は反論の反応アプリを開発しながら遭遇します.

コンポーネント
react-hot-toast 美しい通知(乾杯)を表示する小さくてシンプルなパッケージです.ここにバインディングです<Toaster> コンポーネントtoast 関数.
module Toaster = {
  // JavaScript equivalent
  // import { Toaster } from 'react-hot-toast'
  @react.component @module("react-hot-toast")
  external make: unit => React.element = "Toaster"

  // import ReactHotToast from 'react-hot-toast'
  @module("react-hot-toast")
  external make: t = "default"

  // ReactHotToast.success("Some string")
  @send external success: (t, string) => unit = "success"
}

// Usage in our app
@react.component
let make = () => {
  <>
    <Toaster />
    <button onClick={_ => Toaster.make->Toaster.success("Success!")} />
  </>
}
我々は、2つの装飾子を追加することから始めます.@react.component and @module("react-hot-toast") . @react.component のいずれかを使用するものと同じです.@module("react-hot-toast") 外部のパッケージからインポートするバインディングを作成しますreact-hot-toast .
我々は、デフォルトの<Toaster> だから私たちはmake 関数はunit , この場合、小文字を意味し、React.element . 最後に、我々は設定"Toaster" それは名前付きの輸出です.
デフォルトのエクスポートreact-hot-toast を取る関数string , しかし、それはまた、成功のような特別なケースのバリアントを持っています.使用@send 我々がそうすることができるデコレータsuccess 関数.これを呼び出すには、最初にToaster.t パラメータを入力し、表示するテキストを渡します.結果のコードはonClick ハンドラ.

小道具で
ほとんどの場合、我々はいくつかの小道具を我々がバインドする反応コンポーネントに渡すことができるようになりたいので react-markdown .
module Markdown = {
  // JavaScript equivalent
  // import ReactMarkdown from 'react-markdown'
  @react.component @module("react-markdown")
  external make: (
    ~children: string,
    ~className: string=?,
  ) => React.element = "default"
}

// Usage in our app
@react.component
let make = () => {
  <Markdown>
    "# I'm an H1"
  </Markdown>
}
小道具のない束縛と比較した違いはmake 関数は以下を受け入れる
  • children: string - コンポーネントの子、つまりコンテンツはstring HTMLへのマークダウンとして解析される
  • className: string=? - The ?className オプションのプロパティです
  • また、使用していることに注意してください"default" パッケージのデフォルトエクスポートをインポートします.

    反応フック
    反応フックへの束縛は他のどの関数も結合するようです.以下にバインドの例を示します use-dark-mode .
    module DarkMode = {
      type t = {
        value: bool,
        toggle: unit => unit,
      }
    
      // JavaScript equivalent
      // import UseDarkMode from 'use-dark-mode'
      @module("use-dark-mode") external useDarkMode: bool => t = "default"
    }
    
    @react.component
    let make = () => {
      let darkMode = DarkMode.useDarkMode(false)
    
      <div>
        {React.string(darkMode.value ? "Dark and sweet" : "Light and clean")}
        <button onClick={_ => darkMode.toggle()}>
          {React.string("Flip the switch")}
        </button>
      </div>
    }
    
    結合のためにモジュールを作成する必要はありません、しかし、私はそれが結合をよりきれいにカプセル化すると思います.フックはbool 初期状態と返り値DarkMode.t . DarkMode.trecord しかし、これらは、ランタイムコストなしでJavaScriptオブジェクトにコンパイルし、別のメソッドを使用してReScript objects .

    支柱を描く
    プロップは、反応フックの導入に続いて、もはや一般的ではありません、しかし、我々は時々時々遭遇します.以下にバインドの例を示します Formik .
    module Formik = {
      type renderProps<'values> = {values: 'values}
    
      // JavaScript equivalent
      // import { Formik } from 'formik'
      @react.component @module("formik")
      external make: (
        ~children: renderProps<'values> => React.element,
        ~initialValues: 'values,
      ) => React.element = "Formik"
    }
    
    type form = {name: string}
    
    @react.component
    let make = () => {
      <Formik initialValues={{name: "React"}}>
        {({values}) => {
          <div> {React.string(values.name)} </div>
        }}
      </Formik>
    }
    
    現在、それはより複雑になっています、そして、我々がAを使っている最初ですtype parameter AKAジェネリック!反応コンポーネントを定義することから始めます<Formik> . これは2つの小道具を受け付けます.
  • children: renderProps<'values> => React.element - 子は、renderProps 記録する'values ) を返し、React.element
  • initialValues: 'values - フォームの初期データを持つレコード
  • 値の型を定義するtype form そしてそのタイプのレコードをFormek ' sに渡しますinitialValues プロップこの後、values レンダリングプロップは自動的にタイプform と同じ型パラメータを使用するのでinitialValues 我々の束縛で.
    注意: Formekはフォームを作成するための複数のAPIを持ち、これは完全に機能するバインドではありません.それはちょうどレンダリング小道具の使用を示すことです.

    グローバル変数
    時にはグローバル変数に接続して接続する必要があります.これは私たちが前のポストでしたことです.私はコード例をここに含めます、しかし、あなたがそれについてもっと学びたいならば、それは前のポストを見ます.
    @val @scope("localStorage") external getItem: string => Js.Nullable.t<string> = "getItem"
    @val @scope("localStorage") external setItem: (string, string) => unit = "setItem"