MS_ASPNETCoreReactReduxTemplate - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki

ASP.NET Core React+Reduxテンプレヌト

抂芁

https://github.com/OpenTouryoProject/SampleProgram/tree/master/Template/SPATemplate/ReactReduxTemplate

※ JavaScript Services も流行っおないので削陀したした。
 IDE に暙準のテンプレヌトずしお衚瀺されなくなっおいるdotnet new コマンドには残っおいる。

構成

フォルダ

ReactReduxTemplate
 ├ ClientApp
   ├ components
     ├ Counter.tsx
     ├ FetchData.tsx
     ├ Home.tsx
     ├ Layout.tsx
     └ NavMenu.tsx
   ├ css
     └ site.css
   ├ store
     ├ Counter.ts
     ├ index.ts
     └ WeatherForecasts.ts
   ├ boot-client.tsx
   ├ boot-server.tsx
   ├ configureStore.ts
   └ routes.tsx
 ├ Controllers
   ├ HomeController.cs
   └ SampleDataController.cs
 ├ Views
   ├ Home
     └ Index.cshtml
   ├ Shared
     ├ _Layout.cshtml
     └ Error.cshtml
   ├ _ViewImports.cshtml
   └ _ViewStart.cshtml
 ├ wwwroot
   └ favicon.ico
 ├ .gitignore
 ├ appsettings.Development.json
 ├ appsettings.json
 ├ npm-shrinkwrap.json
 ├ package.json
 ├ Program.cs
 ├ ReactReduxTemplate.csproj
 ├ Startup.cs
 ├ tsconfig.json
 ├ webpack.config.js
 └ webpack.config.vendor.js

スタック

ASP.NET Core React.jsテンプレヌト に
Reduxが远加されおいる。

補足Redux ずは䜕を解決するものか: 「React に Redux を足す」
ずいう話の動機を抌さえおおく。

【React だけの堎合の問題】

   App
    ├ Header ─ ナヌザヌ名を衚瀺したい
    ├ Sidebar
    └ Main
         └ Content
              └ Form ─ ログむン凊理を行う

   Form の結果を Header に反映したい
     → 状態を【共通の祖先App】に持ち䞊げるlifting state up
     → props で【䜕段も䞋たで枡す】prop drilling★ 苊痛
【Redux の解】
   状態を【1 箇所Store】に集め、
   どのコンポヌネントからも盎接読める曎新できるようにする

      Store唯䞀の状態
        ▲       │
        │Action │state
        │       â–Œ
   ┌────┮───────────┐
   Header   Form   Content 


Redux の 3 原則:

 ① Single source of truth  
 状態は【1 ぀の Store】に集玄する
 ② State is read-only      
 状態は盎接曞き換えず、【Action を投げる】
 ③ Changes with pure functions 
 Reducer は【玔粋関数】
                                  (state, action) => 新しい state

(state, action) => newState ずいう圢は、
C# の Aggregate畳み蟌みず同じ発想である。
副䜜甚がないため、同じ Action の列を再生すれば同じ状態になる
——これが Time Travel Debugging を可胜にしおいる。

詳现

≒ ASP.NET Core React.jsテンプレヌト からの倉曎点

远加

以䞋が远加されおいる。

├ store
  ├ Counter.ts
  ├ index.ts
  └ WeatherForecasts.ts
├ boot-client.tsx
├ boot-server.tsx
├ configureStore.ts

store

TypeScript なので、Reducer ではなく Bean 的に Store ずしお定矩するらしい。
実際は、ココで、Reducer、Action、Store が定矩される

  • store フォルダの远加

    store
     ├ Counter.ts
     ├ index.ts
     └ WeatherForecasts.ts
    
    • 1 ぀の JSON ではなく耇数の Bean ずしお定矩しおある。
    • Store、Action、Reducer の定矩
    • 非同期凊理は Reducer に曞かれるのでコチラ。
  • configureStore.ts ファむルの远加

    • store の蚭定
    • reducer を Hot Reloading で構築

補足この構成は「Ducks パタヌン」である: 原文が「Bean 的に」ず
衚珟しおいる構成には、名前が付いおいる。

【埓来の Redux機胜ごずにファむルを分ける】
   actions/counter.ts      
 Action Creator
   reducers/counter.ts     
 Reducer
   constants/counter.ts    
 Action Type の定数
     → 1 ぀の機胜を远加するのに【3 ファむル觊る】★ 面倒

【Ducks パタヌン機胜ごずに 1 ファむルにたずめる】
   store/Counter.ts        
 Action Type + Action Creator + Reducer + 型
     → 【関連するものが 1 箇所】にある
     → 原文の構成はこちら

index.ts の圹割は、各 Reducer を結合するこずである
combineReducers。

// store/index.ts
export interface ApplicationState {
    counter: Counter.CounterState;
    weatherForecasts: WeatherForecasts.WeatherForecastsState;
}
export const reducers = {
    counter: Counter.reducer,
    weatherForecasts: WeatherForecasts.reducer,
};

移行メモ「非同期凊理は Reducer に曞かれる」は正確でない: 原文の
この蚘述は、Redux の原則に照らすず逆である。

【Redux の原則】
   Reducer は【玔粋関数】でなければならない
     → 同じ入力なら垞に同じ出力
     → 【副䜜甚API 呌び出しを曞いおはならない】★

【では非同期はどこに曞くのか】
   ミドルりェアredux-thunkを通した
   【Action Creator】に曞く
// store/WeatherForecasts.tsDucks なので同じファむルにある
export const actionCreators = {
    // ↓ これは「関数を返す Action Creator」 thunk
    requestWeatherForecasts: (startDateIndex: number) =>
        async (dispatch, getState) => {
            dispatch({ type: 'REQUEST_WEATHER_FORECASTS', startDateIndex });
            const res = await fetch(`/api/SampleData/WeatherForecasts`);
            const forecasts = await res.json();
            dispatch({ type: 'RECEIVE_WEATHER_FORECASTS', forecasts });
        }
};

// Reducer は玔粋なたた受け取った Action で state を䜜り盎すだけ
export const reducer = (state = initialState, action) => {
    switch (action.type) {
        case 'REQUEST_WEATHER_FORECASTS': return { ...state, isLoading: true };
        case 'RECEIVE_WEATHER_FORECASTS': return { ...state, isLoading: false,
                                                   forecasts: action.forecasts };
        default: return state;
    }
};

原文の構成では䞡方が同じファむルstore/*.tsにあるため、
「Reducer に曞かれる」「store フォルダのファむルに曞かれる」
ずいう意味だった可胜性が高い。堎所ずしおは原文の通りである。

boot

  • boot-client.tsx の远加
    boot に察応。

  • boot-server.tsx の远加
    サヌバヌサむド・レンダリングずいう技術を
    利甚するのために実装されおいるらしいが詳现䞍明。

補足「詳现䞍明」ぞの回答SSR ずは: **サヌバヌ サむド
レンダリングSSR**の仕組みを補っおおく。

【CSRクラむアント サむド レンダリング 通垞の SPA】
   ブラりザ → サヌバ GET /
   サヌバ  → ブラりザ <div id="react-app">Loading...</div> + JS
   ブラりザ JS をダりンロヌド → 実行 → API 呌び出し → 描画
     → 【最初は空癜】。衚瀺たでに時間がかかる
     → 怜玢゚ンゞンが䞭身を読めない堎合がある

【SSRサヌバヌ サむド レンダリング】
   ブラりザ → サヌバ GET /
   サヌバ 【Node.js 䞊で React を実行】し、HTML を組み立おる
   サヌバ  → ブラりザ 完成した HTML + JS
   ブラりザ すぐ衚瀺できる。その埌 JS が「氎和hydrate」しお
             むンタラクティブになる ★

boot-client.tsx ず boot-server.tsx に分かれる理由:

boot-client.tsx 
 ブラりザで動く。hydrate する
boot-server.tsx 
 【Node.js で動く】。HTML 文字列を返す

  → 同じコンポヌネントを、2 ぀の環境で実行する
  → だから【isomorphic】同型ずいう語が出おくる
    [ASP.NET Core React.jsテンプレヌト] の isomorphic-fetch

なぜ ASP.NET Core でこれが廃れたのか:

【本番環境で Node.js プロセスを抱えるこずになる】
   ・.NET のプロセスから Node を起動しお面倒を芋る
   ・スケヌル、監芖、障害察応の察象が 2 倍になる
   ・[JavaScript Services] が廃止された理由そのもの ★

【珟圚の解】
   SSR が芁るなら、【Node.js 偎で完結させる】
     → Next.js / Remix / Nuxt / Angular Universal
     → .NET は【API を提䟛するだけ】に培する

configureStore.ts が client/server 䞡方から䜿われるのも
この構成のためで、Store を共有できる圢にする必芁があった
サヌバで䜜った初期状態を HTML に埋め蟌み、クラむアントで埩元する。

差分

package

redux ず、redux-thunk の導入により、
既存の WebAPI 呌び出しの非同期凊理を行う、
isomorphic-fetch ず、json-loader が削陀されおいる。

  • 远加

    • redux

    • redux-thunk

    • react-redux

    • react-router-redux

    • node-noop

    • history

    • domain-task

  • 削陀

    • isomorphic-fetch
    • json-loader

補足各パッケヌゞの圹割ず珟況:

パッケヌゞ 圹割 珟況
redux Store / Reducer の䞭栞 珟圹。ただし Redux Toolkit 経由が掚奚
redux-thunk 非同期の Action を可胜にする Redux Toolkit に同梱された
react-redux React ず Redux を繋ぐconnect / Hooks 珟圹
react-router-redux ルヌティング状態を Store に入れる 廃止connected-react-router も珟圚は非掚奚
node-noop Node 環境で䜕もしないダミヌ 䞍芁
history HTML5 History の抜象化 react-router に内包された
domain-task SSR で非同期完了を埅぀ 廃止JavaScript Services 由来

移行メモ原文の因果関係: 「redux-thunk の導入により
isomorphic-fetch が削陀された」ず読めるが、
fetch 自䜓は䟝然ずしお必芁である
thunk の䞭で API を呌ぶため。

【実際に起きたこず】
   ・fetch の呌び出し堎所が、コンポヌネント → thunk に移った
   ・SSR 察応のため、isomorphic-fetch ではなく
     【domain-task】が非同期の完了远跡を担うようになった
     domain-task は内郚で fetch をラップする

json-loader の削陀は別の理由で、
webpack 2 以降、JSON のむンポヌトが暙準サポヌトされたためである。

仕様

  • Redux 化
    setState を connect 関数に倉曎
    props を䜿甚しお state ず function を枡す。

    • Counter.tsx
    • FetchData.tsx
  • BrowserRouter を䜿甚したペヌゞング凊理の远加

    • routes.tsxURL にペヌゞ番号を組蟌む
    • FetchData.tsx
  • サヌバヌサむド・レンダリング

    • index.html
    • boot-server.tsx

補足connect ず、珟圚の曞き方: 原文の「setState を
connect 関数に倉曎」ずいう芁玄は、Redux 化の本質を突いおいる。

// 圓時connect で state ず dispatch を props に泚入する
class Counter extends React.Component<CounterProps> {
    render() {
        return <button onClick={() => this.props.increment()}>
                 Count: {this.props.count}
               </button>;
    }
}
export default connect(
    (state: ApplicationState) => state.counter,        // mapStateToProps
    CounterStore.actionCreators                        // mapDispatchToProps
)(Counter);
【connect が意味しおいたこず】
   ・コンポヌネントは【Store を知らない】props しか芋ない
      → 【テストしやすい】props を枡すだけ★
      → [䟝存性反転原則] ず同じ発想
   ・connect が「繋ぐ」圹をする

珟圚は Hooks を䜿うconnect も動くが、非掚奚寄り。

// 珟圚react-redux の Hooks
export function Counter() {
    const count = useSelector((s: RootState) => s.counter.value);
    const dispatch = useDispatch();
    return <button onClick={() => dispatch(increment())}>Count: {count}</button>;
}

さらに珟圚は Redux ToolkitRTKが公匏掚奚である。

// Redux ToolkitAction Type / Action Creator / Reducer が䞀床に䜜れる
const counterSlice = createSlice({
    name: 'counter',
    initialState: { value: 0 },
    reducers: {
        increment: state => { state.value += 1 },   // ← 盎接曞き換えお良い ★
    },
});
export const { increment } = counterSlice.actions;
【state.value += 1 ず曞けるのはなぜか】
   RTK は内郚で【Immer】を䜿っおいる
     → 「曞き換えたように芋える」コヌドから、
       自動的に【新しいオブゞェクト】を䜜る
     → 玔粋関数の原則は守られおいる
     → スプレッド構文{...state, value: state.value + 1}の
       煩雑さから解攟される

補足そもそも Redux は今も必芁か: これが珟圚の実務での
最初の問いになる。

【Redux が生たれた頃2015の状況】
   ・React に状態管理の暙準がなかった
   ・Context API が未成熟
   ・デヌタ取埗のためのラむブラリもなかった
     → 【党郚 Redux でやる】こずになった
        → API のレスポンスたで Store に入れる
        → ボむラヌプレヌトが膚倧に ★

珟圚は、状態を皮類で分けお考える。

状態の皮類 䟋 珟圚の掚奚
サヌバの状態 API から取ったデヌタ TanStack Query / SWR ★
URL の状態 怜玢条件、ペヌゞ番号 URL のク゚リ文字列react-router
フォヌムの状態 入力䞭の倀 React Hook Form
UI の局所状態 開閉、タブ遞択 useState
アプリ党䜓の状態 ログむン ナヌザヌ、テヌマ Context たたは Zustand / Jotai
耇雑な業務状態 倧芏暡な線集画面、履歎 Redux Toolkit今も有効
【原文のテンプレヌトの WeatherForecasts】
   → これは【サヌバの状態】である
   → 珟圚なら Redux ではなく TanStack Query を䜿う
   → Store に入れる必芁はない

Redux が今も適する堎面:

・状態遷移が耇雑で、【履歎・取り消しが芁る】
・倚くの画面が【同じ状態を共有する】
・状態の倉化を【監査・蚘録したい】Action がログになる
・倧芏暡チヌムで【状態倉曎の経路を匷制したい】★

「Redux を䜿わない」こずは、珟圚では正しい遞択であり埗る——
ずいう点が、圓時ずの最倧の違いである。

参考

開発基盀郚䌚 Wiki

JavaScript > React / Redux

この凊理を、create-react-appで䜜成し、掘り䞋げを行う。

React のセカンド・ステップ

Microsoft Learn


Tags: 移行, .NET開発, .NET Core, ASP.NET, ASP.NET Web API, ASP.NET SPA, JavaScript

⚠ **GitHub.com Fallback** ⚠