😆
ReduxがAngularに公式ポーティングされた
2024/9/14に下記のXのポストを発見しました。
なんと、Redux公式でAngularへのポーティングされました。
筆者はこれまでNgRxを使ってきましたが、React民でRedux Toolkitがお気に入りだったのでこれは朗報でした。
早速、使ってみます。
セットアップ
公式のGetting Startedの通り進めていきます。
Angularプロジェクトの作成
Angularのプロジェクトを作ります。
Angular 17.3 以上である必要があります。
> npx -p @angular/cli ng new angular-redux-example
Need to install the following packages:
@angular/cli@18.2.4
Ok to proceed? (y) y
? Which stylesheet format would you like to use? Sass (SCSS) [ https://sass-lang.com/documentation/syntax#scss]
? Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? no
CREATE angular-redux-example/README.md (1080 bytes)
~~~~~~
⠸ Installing packages (bun)...
✔ Packages installed successfully.
Successfully initialized git.
angular reduxのインストール
下記コマンドでangular-reduxをインストールします。
> npx ng add @reduxjs/angular-redux@latest
✔ Determining Package Manager
› Using package manager: bun
✔ Loading package information from registry
✔ Confirming installation
✔ Installing package
CREATE src/app/store/counter-slice.ts (648 bytes)
CREATE src/app/store/index.ts (291 bytes)
UPDATE src/app/app.config.ts (423 bytes)
UPDATE package.json (1149 bytes)
✔ Packages installed successfully.
下記パッケージがインストールされます。
package.json
{
"@reduxjs/angular-redux": "^1.0.0",
"@reduxjs/toolkit": "^2.2.7",
"redux": "^5.0.0"
}
また、Reduxのcountサンプルを作ってくれます。
src/app/app.config.ts
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideRedux } from '@reduxjs/angular-redux';
import { store } from './store';
export const appConfig: ApplicationConfig = {
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideRedux({ store })]
};
src/app/store/counter-slice.ts
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './counter-slice'
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
src/app/store/index.ts
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './counter-slice'
import {injectDispatch, injectSelector} from "@reduxjs/angular-redux";
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export const injectAppDispatch = injectDispatch.withTypes<AppDispatch>();
export const injectAppSelector = injectSelector.withTypes<RootState>();
サンプル実装
counterReducerのサンプルコードを使って、最小限のコードを書いてみます。
src/app/app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {decrement, increment} from "./store/counter-slice";
import {injectAppDispatch, injectAppSelector, RootState} from "./store";
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
template: `
<h1>
<button (click)="increment()">+1</button>
Count: {{$count()}}
<button (click)="decrement()">-1</button>
</h1>
`,
styleUrl: './app.component.scss'
})
export class AppComponent {
title = 'angular-redux-example';
$count = injectAppSelector((state) => {
return state.counter.value
});
dispatch = injectAppDispatch();
increment = () => {
this.dispatch(increment());
}
asyncIncrement = async () => {
await this.dispatch(async (dispatch, state) => {
dispatch(increment());
await Promise.resolve();
dispatch(increment());
});
console.log('done');
}
decrement = () => {
this.dispatch(decrement());
}
}
injectAppSelector()
でstateは取り出せます。
戻り値はSignalになっています。
injectAppDispatch()
でdispatch()できます。
非同期Actionも使えます。
上記コードはGithubのリポジトリにあります。
Discussion