React + r2wc in Blazor

react, r2wc/react-to-web-component, WebComponent, Blazor Server App - InteractiveServer, React in Blazor,

引言

NET8 Blazor Web App - InteractiveSever 模式執行用 react + r2wc 打包的 web-componnet。

相關文章

React In Blazor

React In Blazor - DidUpdate

參考文件


開發環境

  • IDE: Visual Studio 2022

  • 平台: NET8

  • 骨架: Blazor Web App - InteractiveServer

  • 前端: React.v18.2

  • bundler: webpack.v5.88


關鍵程式碼紀錄

前端 ReactWidgets 部份

React 元件測試通訊範例。

ReactWidgets\src\WebSample.js
import React, { useState } from 'react'
import cx from "./WebSample.module.css"

//§ 機制:WebComponent 與 Blazor 通訊 
//※ web-component 的 props 名稱需全小寫,否則通訊會失敗。
export default function WebSample({ dot_net_object: /* object */, foo /* string */, on_bar /* (payload) => void */ }) {
  const [cnt, setCnt] = useState(1)

  return (
    <div className={cx.wall}>
      <h5>來自外面:{foo || 'nil'}</h5>
      <button className={cx.button} onClick={handleClick}>送出訊息到 Blazor 後端</button>
    </div>
  )

  function handleClick() {
    console.log('WebSample.handleClick', { dot_net_object, cnt })
    setCnt(c => c + 1)

    //## events up
    //※ 透過 dotNetObject 送訊息回去,可以直達 Blazor 後端。
    const message = `[${cnt}] 透過 dotNetObject 送訊息到 Blazor 後端。 - ${foo}`;
    dot_net_object.invokeMethodAsync('JsInvokeBarEvent', message);

    on_bar(message) // 自己送訊息回去只能送到前端!
  }
}

使用 r2wc 把 react 元件打包轉譯成 web-component 元件。

ReactWidgets\src\_main.js
"use strict";
import r2wc from '@r2wc/react-to-web-component'
import WebSample from './WebSample'

//#region 註冊 Web Components
///※註1:web-component 的命名需全小寫。
///※註2:因為 web-component 是純前端元件,經由 function 向上傳遞訊息只能送到前端。
customElements.define("web-sample", r2wc(WebSample, {
  props: {
    dot_net_object: "object", // 透過 dotNetObject 送訊息回去,可以直達 Blazor 後端。
    foo: "string",
    on_bar: "function", // 只作用在前端!
  },
}));

Blazor Web App 部份

一般來說可以直接在 html 使用 web-component,然因 Blazor 與 html 不能直接互動,需間接操作。

TestWebSample.razor
@implements IDisposable
@inject IJSRuntime jsr

<div class="pa-2 my-2" style="border: solid 2px blue; border-radius: 8px">
  <h3>TestWebSample</h3>
  <p>測試 R2WC 通訊能力</p>

  <MudTextField Label="輸入些文字" @bind-Value=foo />

  <web-sample @ref=refWebSample foo=@foo />
  @* ※若沒有透過 dotNetObject 通訊需求的話,直接使用 web-componnet 即可不用再包裝一層。 *@

  <p>來自<code>web-sample</code>送上來的訊息, barMessage: @barMessage</p>
</div>

@code {
  //## Resource
  ElementReference refWebSample;
  IDisposable? dotNetObject = null;

  //## State
  string foo = "從外面給值";
  string barMessage = string.Empty;

  public void Dispose()
  {
    dotNetObject?.Dispose();
    dotNetObject = null;
  }

  protected override void OnInitialized()
  {
    dotNetObject = DotNetObjectReference.Create(this);
  }

  protected override async Task OnAfterRenderAsync(bool firstRender)
  {
    if (firstRender)
      await jsr.InvokeVoidAsync("assignDotNetObject", refWebSample, dotNetObject);
      //※ web-component 初始化 - 指定 dotNetObject。
      // 必需間接使用 JSInterop 轉換才可 NET → JS。
  }

  /// <summary>
  /// 當 React 元件有訊息送上來時觸發。
  /// </summary>
  [JSInvokable]
  public async Task JsInvokeBarEvent(string message)
  {
    barMessage = message;
    await InvokeAsync(StateHasChanged);
  }
}

必需間接使用 JSInterop 轉換才可 NET → JS。

Components\App.razor
<!DOCTYPE html>
<html lang="zh-hant">
<head>
  ...略...
</head>
<body>
  ...略...

  @* <!-- To register dotNetObject for web-components. --> *@
  <script>
    function assignDotNetObject(theWebComponent /* element */, dotNetObject /* object */) {
      theWebComponent.dot_net_object = dotNetObject;
    }
  </script>
</body>
</html>


沒圖沒真象

(EOF)

Last updated