「.NET 開発基盤部会 Wiki」は、「Open棟梁Project」,「OSSコンソーシアム .NET開発基盤部会」によって運営されています。
Redux対応を行う。
以下で生成したコードを修正して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対応の修正内容を教えてください。npm install @reduxjs/toolkit react-redux
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.reducerimport { 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.dispatchsrc/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>,
)
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}
/>
);
};
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
```
「修正内容1」で変更済み。
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>
)
}