「[[.NET 開発基盤部会 Wiki>http://dotnetdevelopmentinfrastructure.osscons.jp]]」は、「[[Open棟梁Project>https://github.com/OpenTouryoProject/]]」,「[[OSSコンソーシアム .NET開発基盤部会>https://www.osscons.jp/dotNetDevelopmentInfrastructure/]]」によって運営されています。 -戻る --[[React]] -> [[Redux]] -戻る([[React]] -> [[Redux]]) --...[[Reactの5thステップ]] --[[Reactのファースト・ステップ2]] --Reactのセカンド・ステップ2 --[[Reactのサード・ステップ2]] *目次 [#z47a1177] #contents *概要 [#w9bb8b28] [[Redux]]対応を行う。 *詳細 [#e2486216] **修正内容1 [#ua59fb6a] -入力:プロンプト 以下で生成したコードを修正して3分割のヘッダ、左メニュー、メインエリアの画面のRedux対応を行うにはどのようにすればよいですか? npm create vite@latest my-app -- --template react-ts なお、「react-router-dom」が導入されており、左メニューのリンクを押下するとメインエリアが画面遷移するようになっている。 // src/App.tsx import { BrowserRouter, Routes, Route } from 'react-router-dom' import Header from './components/Header' import Sidebar from './components/Sidebar' import Home from './pages/Home' import About from './pages/About' import Settings from './pages/Settings' import { Counter } from './pages/Counter' import './App.css' function App() { return ( <BrowserRouter> <div className="app-container"> <Header /> <div className="body-container"> <Sidebar /> <main className="main-area"> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/settings" element={<Settings />} /> <Route path="/counter" element={<Counter />} /> </Routes> </main> </div> </div> </BrowserRouter> ) } export default App 追加フィアルの構成は以下の通り .\src\components\Header.tsx .\src\components\Sidebar.tsx .\src\pages\Home.tsx .\src\pages\About.tsx .\src\pages\Settings.tsx .\src\pages\Counter.tsx Redux対応の修正内容を教えてください。 -出力:変更点のまとめ --store/counterSlice.ts:CounterのState・Reducerを定義 --store/index.ts:Storeを構成、型をエクスポート --main.tsx:<Provider store={store}> でアプリ全体をラップ --pages/Counter.tsx:useState → useSelector/useDispatch に置き換え ***[[Redux]]のインストール [#j59d59a9] npm install @reduxjs/toolkit react-redux ***State・Reducerを定義 [#l7a0622f] -src/store/counterSlice.ts(新規作成) import { createSlice } from '@reduxjs/toolkit' import type { PayloadAction } from '@reduxjs/toolkit' interface CounterState { value: number } const initialState: CounterState = { value: 0, } const counterSlice = createSlice({ name: 'counter', initialState, reducers: { increment: (state) => { state.value += 1 }, decrement: (state) => { state.value -= 1 }, incrementByAmount: (state, action: PayloadAction<number>) => { state.value += action.payload }, reset: (state) => { state.value = 0 }, }, }) export const { increment, decrement, incrementByAmount, reset } = counterSlice.actions export default counterSlice.reducer -src/store/index.ts(新規作成) import { configureStore } from '@reduxjs/toolkit' import counterReducer from './counterSlice' export const store = configureStore({ reducer: { counter: counterReducer, }, }) // 型エクスポート(useSelector / useDispatch で使用) export type RootState = ReturnType<typeof store.getState> export type AppDispatch = typeof store.dispatch ***Provider で App 囲む [#v67f7a43] src/main.tsx(修正) Provider で App 全体を囲みます。 tsximport { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import { Provider } from 'react-redux' // ← 追加 import { store } from './store' // ← 追加 import App from './App' import './index.css' createRoot(document.getElementById('root')!).render( <StrictMode> <Provider store={store}> {/* ← 追加 */} <App /> </Provider> </StrictMode>, ) ***Counterを修正 [#kad7ed36] src/pages/Counter.tsx(修正) -以下は何回かエラーをプロンプトで修正している。 -主に、既存コードを活かした修正方法に変更している。 import * as React from 'react'; // Redux 関連のインポート import { useSelector, useDispatch } from 'react-redux'; import type { RootState, AppDispatch } from '../store'; import { increment, reset } from '../store/counterSlice'; // --- 既存コード // Propsの型定義 type CounterProps //= {}; = { currentCount: number; onIncrement: () => void; onReset: () => void; }; // Stateの型定義 type CounterState = { currentCount: number; }; // 以下、Redux修正あり // クラス名称を変更 export class Counter_org extends React.Component<CounterProps, CounterState> { constructor(props: CounterProps) { super(props); this.state = { currentCount: 0 }; } // Reduxのdispatchを呼び出す形に変更 render() { return ( <div> <h1>Counter</h1> <p>This is a simple example of a React component.</p> <p>Current count: <strong>{this.props.currentCount}</strong></p> <button className='btn-primary' onClick={() => this.props.onIncrement()}> Increment </button> <button className='btn-primary' onClick={() => this.props.onReset()}> Reset </button> </div> ); } /*incrementCounter() { this.setState({ currentCount: this.state.currentCount + 1, }); }*/ } // --- Reduxラッパー --- export const Counter = () => { const currentCount = useSelector((state: RootState) => state.counter.value); const dispatch = useDispatch<AppDispatch>(); // dispatch の戻り値を void に吸収させる const handleIncrement = (): void => { dispatch(increment()); }; const handleReset = (): void => { dispatch(reset()); }; return ( <Counter_org currentCount={currentCount} onIncrement={handleIncrement} onReset={handleReset} /> ); }; **修正内容2 [#n2b932a5] -入力:プロンプト Redux対応の効果を観測するためにsrc\pages\Counter.tsxのcount情報をsrc\pages\About.tsxでも使用したい。どの様に変更しますか?以下は前提となる現実装の情報です。 * src\main.tsx ```typescriptreact import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './index.css' import App from './App.tsx' // Redux 関連のインポート import { Provider } from 'react-redux' import { store } from './store' createRoot(document.getElementById('root')!).render( <StrictMode> <Provider store={store}> <App /> </Provider> </StrictMode>, ) ``` * src\store\index.ts ```typescript import { configureStore } from '@reduxjs/toolkit' import counterReducer from './counterSlice' export const store = configureStore({ reducer: { counter: counterReducer, }, }) // 型エクスポート(useSelector / useDispatch で使用) export type RootState = ReturnType<typeof store.getState> export type AppDispatch = typeof store.dispatch ``` * src\store\counterSlice.ts ```typescript import { createSlice } from '@reduxjs/toolkit' import type { PayloadAction } from '@reduxjs/toolkit' interface CounterState { value: number } const initialState: CounterState = { value: 0, } const counterSlice = createSlice({ name: 'counter', initialState, reducers: { increment: (state) => { state.value += 1 }, decrement: (state) => { state.value -= 1 }, incrementByAmount: (state, action: PayloadAction<number>) => { state.value += action.payload }, reset: (state) => { state.value = 0 }, }, }) export const { increment, decrement, incrementByAmount, reset } = counterSlice.actions export default counterSlice.reducer ``` -出力:変更点のまとめ~ ReduxのstoreはすでにProviderでアプリ全体に提供されているので、~ About.tsxにuseSelectorを追加するだけでcount情報を取得できる。 ***CounterのRedux対応 [#a20b6c4b] 「[[修正内容1>#ua59fb6a]]」で変更済み。 ***AboutにuseSelectorを追加 [#m4fb5fb3] src/pages/About.tsx(修正) tsx// 追加するインポート import { useSelector } from 'react-redux' import type { RootState } from '../store' export default function About() { // Reduxのstoreからcount値を取得 const count = useSelector((state: RootState) => state.counter.value) return ( <div> <h1>About</h1> {/* count情報を表示 */} <p>現在のカウント: {count}</p> </div> ) } *参考 [#b84949d4] -FrontendTemplates/UI/SPA/React at develop · OpenTouryoProject/FrontendTemplates~ https://github.com/OpenTouryoProject/FrontendTemplates/tree/develop/UI/SPA/React