# React + r2wc in Blazor

## 引言

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

## 相關文章

[React In Blazor](https://app.gitbook.com/s/TcGSU3zrSsgSHyCjRHOM/react-in-blazor "mention")

[React In Blazor - DidUpdate](https://app.gitbook.com/s/TcGSU3zrSsgSHyCjRHOM/react-in-blazor-didupdate "mention")

## 參考文件

{% embed url="<https://www.npmjs.com/package/@r2wc/react-to-web-component>" %}

{% embed url="<https://www.thomasclaudiushuber.com/2020/02/14/initializing-web-components-in-blazor-via-js-interop/>" %}

{% embed url="<https://learn.microsoft.com/zh-tw/aspnet/core/blazor/javascript-interoperability/call-javascript-from-dotnet?view=aspnetcore-8.0>" %}

{% embed url="<https://learn.microsoft.com/zh-tw/aspnet/core/blazor/javascript-interoperability/call-dotnet-from-javascript?view=aspnetcore-8.0>" %}

***

## 開發環境

* IDE: Visual Studio 2022
* 平台: NET8
* 骨架: Blazor Web App - InteractiveServer
* 前端: React.v18.2
* bundler: webpack.v5.88

***

## 關鍵程式碼紀錄

### 前端 ReactWidgets 部份&#x20;

React 元件測試通訊範例。

{% code title="ReactWidgets\src\WebSample.js" lineNumbers="true" %}

```jsx
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) // 自己送訊息回去只能送到前端！
  }
}
```

{% endcode %}

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

{% code title="ReactWidgets\src\_main.js" lineNumbers="true" %}

```javascript
"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", // 只作用在前端！
  },
}));
```

{% endcode %}

### Blazor Web App 部份

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

{% code title="TestWebSample.razor" lineNumbers="true" %}

```cshtml
@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);
  }
}
```

{% endcode %}

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

{% code title="Components\App.razor" lineNumbers="true" %}

```cshtml
<!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>
```

{% endcode %}

***

## 沒圖沒真象

<figure><img src="https://3418814621-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwWV7eBNXGCFnnvBsLIlo%2Fuploads%2F5Av8Nv9DUlcoEoo68Zzg%2Fimage.png?alt=media&#x26;token=07bc7dc1-871c-4a36-a6b2-0468bce30315" alt=""><figcaption></figcaption></figure>

(EOF)
