chore(i18n,learn): processed translations (#44851)

This commit is contained in:
camperbot
2022-01-21 01:00:18 +05:30
committed by GitHub
parent f866718a3d
commit 5c868af2b8
1696 changed files with 159426 additions and 69 deletions

View File

@@ -0,0 +1,175 @@
---
id: 5a24c314108439a4d4036154
title: 複数のレデューサーを結合する
challengeType: 6
forumTopicId: 301436
dashedName: combine-multiple-reducers
---
# --description--
アプリの状態が複雑になってくると、状態を複数の断片に分割したいと考えるかもしれません。 しかし、Redux の 1 つ目の原則である「すべてのアプリの状態はストア内の単一の状態オブジェクトに保持される」という原則を忘れてはいけません。 そこで、Redux では複雑な状態モデルの解決策として、レデューサーコンポジションを提供しています。 アプリケーションの状態のさまざまな部分を処理するために複数のレデューサーを定義し、それらのレデューサーを 1 つのルートレデューサーに結合します。 そして、そのルートレデューサーを Redux の `createStore()` メソッドに渡します。
複数のレデューサーを結合できるように、Redux には `combineReducers()` メソッドが用意されています。 このメソッドはオブジェクトを引数として受け取ります。オブジェクトでは、特定のレデューサー関数にキーを関連付けるプロパティを定義します。 Redux は、キーに付けられた名前を、state の関連付けられた部分を示す名前として使用します。
通常は、アプリケーションの状態の各部分について、それらを何らかの方法で区別できる場合、またはそれらが一意である場合には、それぞれに対応したレデューサーを作成することをお勧めします。 たとえば、ユーザー認証機能を備えたメモ作成アプリの場合は、あるレデューサーで認証を処理し、別のレデューサーでユーザーが送信しているテキストやメモを処理します。 このようなアプリケーションの `combineReducers()` メソッドは、たとえば次のように記述します。
```js
const rootReducer = Redux.combineReducers({
auth: authenticationReducer,
notes: notesReducer
});
```
これで、キー `notes` には、メモに関連付けられて `notesReducer` によって処理されるすべての状態が含まれます。 このようにして複数のレデューサーを構成し、より複雑なアプリケーション状態を管理することができます。 この例では、Redux ストアに保持される状態は、`auth` プロパティと `notes` プロパティを含む単一のオブジェクトになります。
# --instructions--
コードエディターに `counterReducer()` 関数と `authReducer()` 関数が Redux ストアとともに用意されています。 `Redux.combineReducers()` メソッドを使用して `rootReducer()` 関数の記述を完成させてください。 `counterReducer``count` というキーに割り当て、`authReducer``auth` というキーに割り当ててください。
# --hints--
`counterReducer` で、`state` を 1 増やすか 1 減らします。
```js
assert(
(function () {
const initialState = store.getState().count;
store.dispatch({ type: INCREMENT });
store.dispatch({ type: INCREMENT });
const firstState = store.getState().count;
store.dispatch({ type: DECREMENT });
const secondState = store.getState().count;
return firstState === initialState + 2 && secondState === firstState - 1;
})()
);
```
`authReducer` で、`authenticated``state``true``false` の間で切り替えます。
```js
assert(
(function () {
store.dispatch({ type: LOGIN });
const loggedIn = store.getState().auth.authenticated;
store.dispatch({ type: LOGOUT });
const loggedOut = store.getState().auth.authenticated;
return loggedIn === true && loggedOut === false;
})()
);
```
ストアの `state` に、数値を保持する `count` とオブジェクトを保持する `auth` の 2 つのキーを持たせます。 `auth` オブジェクトにはブール値を保持する `authenticated` プロパティを持たせます。
```js
assert(
(function () {
const state = store.getState();
return (
typeof state.auth === 'object' &&
typeof state.auth.authenticated === 'boolean' &&
typeof state.count === 'number'
);
})()
);
```
`rootReducer` を、`counterReducer``authReducer` を結合する関数にします。
```js
(getUserInput) =>
assert(
(function () {
const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index'));
return (
typeof rootReducer === 'function' &&
noWhiteSpace.includes('Redux.combineReducers')
);
})()
);
```
# --seed--
## --seed-contents--
```js
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const counterReducer = (state = 0, action) => {
switch(action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
};
const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';
const authReducer = (state = {authenticated: false}, action) => {
switch(action.type) {
case LOGIN:
return {
authenticated: true
}
case LOGOUT:
return {
authenticated: false
}
default:
return state;
}
};
const rootReducer = // Define the root reducer here
const store = Redux.createStore(rootReducer);
```
# --solutions--
```js
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const counterReducer = (state = 0, action) => {
switch(action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
};
const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';
const authReducer = (state = {authenticated: false}, action) => {
switch(action.type) {
case LOGIN:
return {
authenticated: true
}
case LOGOUT:
return {
authenticated: false
}
default:
return state;
}
};
const rootReducer = Redux.combineReducers({
count: counterReducer,
auth: authReducer
});
const store = Redux.createStore(rootReducer);
```

View File

@@ -0,0 +1,133 @@
---
id: 5a24c314108439a4d403615b
title: Object.assign を使用してオブジェクトをコピーする
challengeType: 6
forumTopicId: 301437
dashedName: copy-an-object-with-object-assign
---
# --description--
前のいくつかのチャレンジでは配列を扱いましたが、state が `object` の場合にも状態の不変性 (イミュータビリティ) を強制するのに役立つ方法があります。 オブジェクトを扱うのに便利なツールとして `Object.assign()` ユーティリティがあります。 `Object.assign()` は、ターゲットオブジェクトとソースオブジェクトを受け取り、ソースオブジェクトからターゲットオブジェクトにプロパティをマップします。 一致するプロパティはすべて、ソースオブジェクト内のプロパティによって上書きされます。 この方法はオブジェクトのシャローコピーを作成するのによく使用されます。1 つ目の引数として空のオブジェクトを渡し、その後にコピーしたいオブジェクトを渡します。 例:
```js
const newObject = Object.assign({}, obj1, obj2);
```
この例では、新しい `object` として `newObject` を作成します。このオブジェクトには、現在 `obj1``obj2` に存在するプロパティが含まれています。
# --instructions--
Redux の state とアクションが、`state` に対して `object` を処理するように変更されました。 `status` プロパティを文字列 `online` に設定する、タイプが `ONLINE` のアクションに対して、新しい `state` オブジェクトを返すようにコードを編集してください。 チャレンジを完了するには `Object.assign()` を使用してください。
# --hints--
Redux ストアが存在し、1 行目で宣言された `defaultState` オブジェクトに等しい state で初期化する必要があります。
```js
assert(
(function () {
const expectedState = {
user: 'CamperBot',
status: 'offline',
friends: '732,982',
community: 'freeCodeCamp'
};
const initialState = store.getState();
return DeepEqual(expectedState, initialState);
})()
);
```
`wakeUp``immutableReducer` はどちらも関数である必要があります。
```js
assert(typeof wakeUp === 'function' && typeof immutableReducer === 'function');
```
タイプ `ONLINE` のアクションのディスパッチで、state 内のプロパティ `status``online` に更新します。state をミューテートさせてはいけません。
```js
assert(
(function () {
const initialState = store.getState();
const isFrozen = DeepFreeze(initialState);
store.dispatch({ type: 'ONLINE' });
const finalState = store.getState();
const expectedState = {
user: 'CamperBot',
status: 'online',
friends: '732,982',
community: 'freeCodeCamp'
};
return isFrozen && DeepEqual(finalState, expectedState);
})()
);
```
`Object.assign` を使用して新しい state を返します。
```js
(getUserInput) => assert(getUserInput('index').includes('Object.assign'));
```
# --seed--
## --seed-contents--
```js
const defaultState = {
user: 'CamperBot',
status: 'offline',
friends: '732,982',
community: 'freeCodeCamp'
};
const immutableReducer = (state = defaultState, action) => {
switch(action.type) {
case 'ONLINE':
// Don't mutate state here or the tests will fail
return
default:
return state;
}
};
const wakeUp = () => {
return {
type: 'ONLINE'
}
};
const store = Redux.createStore(immutableReducer);
```
# --solutions--
```js
const defaultState = {
user: 'CamperBot',
status: 'offline',
friends: '732,982',
community: 'freeCodeCamp'
};
const immutableReducer = (state = defaultState, action) => {
switch(action.type) {
case 'ONLINE':
return Object.assign({}, state, {
status: 'online'
});
default:
return state;
}
};
const wakeUp = () => {
return {
type: 'ONLINE'
}
};
const store = Redux.createStore(immutableReducer);
```

View File

@@ -0,0 +1,61 @@
---
id: 5a24c314108439a4d403614b
title: Redux のストアを作成する
challengeType: 6
forumTopicId: 301439
dashedName: create-a-redux-store
---
# --description--
Redux は、React をはじめとするさまざまなウェブテクノロジーで使用できる状態管理フレームワークです。
Redux には、アプリケーションの状態全体を担う単一の state オブジェクトがあります。 たとえば、10 個のコンポーネントを持つ React アプリがあり、それぞれのコンポーネントが各自のローカルの state を持っている場合でも、アプリ全体の状態は Redux の `store` に格納されている単一の state オブジェクトによって定義されます。 Redux を学ぶ際は、次のことを 1 つ目の重要な原則として理解する必要があります。つまり、「Redux ストアはアプリケーションの状態に関して信頼できる単一の情報源である」ということです。
これはまた、アプリの任意の部分で状態を更新したいときはいつでも、**必ず** Redux ストアを通じて更新しなければならないことを意味します。 一方向のデータフローにより、アプリの状態管理を簡単に追跡できるようになります。
# --instructions--
Redux の `store` は、アプリケーションの `state` を保持して管理するオブジェクトです。 Redux オブジェクトに `createStore()` というメソッドがあり、Redux `store` を作成するために使用します。 このメソッドは、必須の引数として `reducer` 関数を受け取ります。 `reducer` 関数についてはこの後のチャレンジで説明します。コードエディターではすでに定義が済んでいて、 単に `state` を引数に取り、`state` を返します。
`store` 変数を宣言し、それを `createStore()` メソッドに割り当て、引数として `reducer` を渡してください。
**注:** エディターのコードでは、ES6 のデフォルト引数の構文を使用して、`5` という値を保持するようにこの state を初期化しています。 デフォルト引数に慣れていない場合は、このトピックについて説明している[カリキュラムの ES6 セクション](https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/es6/set-default-parameters-for-your-functions)を参照してください。
# --hints--
Redux ストアが存在する必要があります。
```js
assert(typeof store.getState === 'function');
```
Redux ストアで、state に対して 5 という値を持たせます。
```js
assert(store.getState() === 5);
```
# --seed--
## --seed-contents--
```js
const reducer = (state = 5) => {
return state;
}
// Redux methods are available from a Redux object
// For example: Redux.createStore()
// Define the store here:
```
# --solutions--
```js
const reducer = (state = 5) => {
return state;
}
const store = Redux.createStore(reducer);
```

View File

@@ -0,0 +1,55 @@
---
id: 5a24c314108439a4d403614d
title: Redux のアクションを定義する
challengeType: 6
forumTopicId: 301440
dashedName: define-a-redux-action
---
# --description--
Redux は状態管理フレームワークであり、したがって state の更新はその中心的な作業の一つです。 Redux では、state の更新はすべて、アクションをディスパッチすることによってトリガーされます。 アクションとは、発生したアクションイベントに関する情報を格納する単なる JavaScript オブジェクトです。 Redux ストアは、これらのアクションオブジェクトを受け取り、それに応じて自身の state を更新します。 場合によっては、Redux のアクションにも何らかのデータが伴うことがあります。 たとえば、ユーザーのログイン後にアクションはユーザー名を伴います。 データは省略可能ですが、アクションには、発生したアクションの「タイプ」を指定する `type` プロパティを持たせる必要があります。
Redux のアクションは、アプリで発生しているイベントに関する情報を Redux ストアに届けるメッセンジャーと捉えることができます。 情報を受け取ったストアは、発生したアクションに基づいて状態を更新する役割を果たします。
# --instructions--
Redux のアクションの記述は簡単で、type プロパティを持つオブジェクトを宣言するだけです。 オブジェクト `action` を宣言し、文字列 `'LOGIN'` を設定したプロパティ `type` を付けてください。
# --hints--
`action` オブジェクトが存在する必要があります。
```js
assert(
(function () {
return typeof action === 'object';
})()
);
```
`action` オブジェクトに、値 `LOGIN` を持つキープロパティ `type` を持たせます。
```js
assert(
(function () {
return action.type === 'LOGIN';
})()
);
```
# --seed--
## --seed-contents--
```js
// Define an action here:
```
# --solutions--
```js
const action = {
type: 'LOGIN'
}
```

View File

@@ -0,0 +1,57 @@
---
id: 5a24c314108439a4d403614e
title: アクションクリエイターを定義する
challengeType: 6
forumTopicId: 301441
dashedName: define-an-action-creator
---
# --description--
アクションを作成したら、次のステップとして Redux ストアにアクションを送信し、状態を更新できるようにします。 Redux では、アクションクリエイターを定義してこの処理を実行します。 アクションクリエイターとは、単にアクションを返す JavaScript 関数です。 別の言い方をすれば、アクションクリエイターはアクションイベントを表すオブジェクトを作成します。
# --instructions--
呼び出されたときに `action` オブジェクトを返す `actionCreator()` という関数を定義してください。
# --hints--
関数 `actionCreator` が存在する必要があります。
```js
assert(typeof actionCreator === 'function');
```
`actionCreator` 関数の実行で、`action` オブジェクトを返します。
```js
assert(typeof action === 'object');
```
返される `action` に、値 `LOGIN` を持つキープロパティ `type` を持たせます。
```js
assert(action.type === 'LOGIN');
```
# --seed--
## --seed-contents--
```js
const action = {
type: 'LOGIN'
}
// Define an action creator here:
```
# --solutions--
```js
const action = {
type: 'LOGIN'
}
const actionCreator = () => {
return action;
};
```

View File

@@ -0,0 +1,85 @@
---
id: 5a24c314108439a4d403614f
title: アクションイベントをディスパッチする
challengeType: 6
forumTopicId: 301442
dashedName: dispatch-an-action-event
---
# --description--
`dispatch` は、アクションを Redux ストアにディスパッチするために使用するメソッドです。 `store.dispatch()` を呼び出して、アクションクリエイターから返された値を渡すと、ストアにアクションが送り返されます。
すでに説明したように、アクションクリエイターは、発生したアクションを指定する type プロパティを持つオブジェクトを返します。 そして、メソッドからアクションオブジェクトを Redux ストアにディスパッチします。 次の行は前のチャレンジの例に基づいていますが、これらは等価であり、どちらもタイプ `LOGIN` のアクションをディスパッチします。
```js
store.dispatch(actionCreator());
store.dispatch({ type: 'LOGIN' });
```
# --instructions--
コードエディターの Redux ストアに初期化済みの state があります。これは `login` プロパティを含んでいるオブジェクトで、プロパティは現在 `false` に設定されています。 また、`loginAction()` というアクションクリエイターがあり、これはタイプ `LOGIN` のアクションを返します。 `dispatch` メソッドを呼び出して `LOGIN` アクションを Redux ストアにディスパッチし、`loginAction()` によって作成されたアクションを渡してください。
# --hints--
関数 `loginAction` の呼び出しで、`type` プロパティが文字列 `LOGIN` に設定されたオブジェクトを返します。
```js
assert(loginAction().type === 'LOGIN');
```
オブジェクトのプロパティ `login``false` に設定してストアを初期化します。
```js
assert(store.getState().login === false);
```
`store.dispatch()` メソッドを使用して、タイプ `LOGIN` のアクションをディスパッチします。
```js
(getUserInput) =>
assert(
(function () {
let noWhiteSpace = getUserInput('index').replace(/\s/g, '');
return (
noWhiteSpace.includes('store.dispatch(loginAction())') ||
noWhiteSpace.includes("store.dispatch({type: 'LOGIN'})") === true
);
})()
);
```
# --seed--
## --seed-contents--
```js
const store = Redux.createStore(
(state = {login: false}) => state
);
const loginAction = () => {
return {
type: 'LOGIN'
}
};
// Dispatch the action here:
```
# --solutions--
```js
const store = Redux.createStore(
(state = {login: false}) => state
);
const loginAction = () => {
return {
type: 'LOGIN'
}
};
store.dispatch(loginAction());
```

View File

@@ -0,0 +1,55 @@
---
id: 5a24c314108439a4d403614c
title: Redux ストアから state を取得する
challengeType: 6
forumTopicId: 301443
dashedName: get-state-from-the-redux-store
---
# --description--
Redux の store オブジェクトには、ストアとのやり取りを可能にするメソッドがいくつか用意されています。 たとえば `getState()` メソッドを使用すると、Redux の store オブジェクトに保持されている現在の `state` を取得できます。
# --instructions--
コードエディターで、前のチャレンジのコードが、より簡潔な内容に書き換えられています。 `store.getState()` を使用して、`store` から `state` を取得し、それを新しい変数 `currentState` に割り当ててください。
# --hints--
Redux ストアで、初期状態で 5 という値を持たせます。
```js
assert(store.getState() === 5);
```
変数 `currentState` が存在し、Redux ストアの現在の state を割り当てる必要があります。
```js
(getUserInput) =>
assert(
currentState === 5 && getUserInput('index').includes('store.getState()')
);
```
# --seed--
## --seed-contents--
```js
const store = Redux.createStore(
(state = 5) => state
);
// Change code below this line
```
# --solutions--
```js
const store = Redux.createStore(
(state = 5) => state
);
// Change code below this line
const currentState = store.getState();
```

View File

@@ -0,0 +1,108 @@
---
id: 5a24c314108439a4d4036150
title: ストアでアクションを処理する
challengeType: 6
forumTopicId: 301444
dashedName: handle-an-action-in-the-store
---
# --description--
アクションが作成されてディスパッチされた後、Redux ストアでは、そのアクションに対してどう応答するかを知る必要があります。 その役割を担うのが `reducer` (リデューサー) 関数です。 Redux のリデューサーは、アクションに応じて行われる state の変更を担います。 `reducer` は、引数として `state``action` を受け取り、常に新しい `state` を返します。 ここで重要なのは、これがレデューサーの**唯一の**役割であると理解することです。 副次的な処理はまったくありません。API のエンドポイントを呼び出すこともなく、驚くようなことも隠されていません。 レデューサーは、state と action を受け取って新しい state を返すだけの純粋な関数です。
Redux のもう一つの重要な原則は「`state` は読み取り専用である」ことです。 言い換えれば、`reducer` 関数は**常に** `state` の新しいコピーを返す必要があり、state を直接変更することは決して許されません。 Redux では状態の不変性を強制してはいませんが、レデューサー関数のコードではプログラマーが状態の不変性を強制する必要があります。 これについてはこのあとのチャレンジで練習します。
# --instructions--
コードエディターに、以前に紹介した例と、`reducer` 関数の冒頭部分があります。 `reducer` 関数の本体を入力して、タイプ `'LOGIN'` のアクションを受け取った場合に `login``true` に設定された state オブジェクトを返すようにしてください。 それ以外の場合は、現在の `state` を返してください。 現在の `state` とディスパッチされた `action` がレデューサーに渡されるため、`action.type` を使用してアクションタイプに直接アクセスすることができます。
# --hints--
関数 `loginAction` の呼び出しで、type プロパティが文字列 `LOGIN` に設定されたオブジェクトを返します。
```js
assert(loginAction().type === 'LOGIN');
```
オブジェクトのプロパティ `login``false` に設定してストアを初期化します。
```js
assert(store.getState().login === false);
```
`loginAction` のディスパッチで、ストアの state の `login` プロパティを `true` に更新します。
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(loginAction());
const afterState = store.getState();
return initialState.login === false && afterState.login === true;
})()
);
```
アクションがタイプ `LOGIN` でない場合、ストアは現在の state を返します。
```js
assert(
(function () {
store.dispatch({ type: '__TEST__ACTION__' });
let afterTest = store.getState();
return typeof afterTest === 'object' && afterTest.hasOwnProperty('login');
})()
);
```
# --seed--
## --seed-contents--
```js
const defaultState = {
login: false
};
const reducer = (state = defaultState, action) => {
// Change code below this line
// Change code above this line
};
const store = Redux.createStore(reducer);
const loginAction = () => {
return {
type: 'LOGIN'
}
};
```
# --solutions--
```js
const defaultState = {
login: false
};
const reducer = (state = defaultState, action) => {
if (action.type === 'LOGIN') {
return {login: true}
}
else {
return state
}
};
const store = Redux.createStore(reducer);
const loginAction = () => {
return {
type: 'LOGIN'
}
};
```

View File

@@ -0,0 +1,133 @@
---
id: 5a24c314108439a4d4036158
title: state を決してミューテートしない
challengeType: 6
forumTopicId: 301445
dashedName: never-mutate-state
---
# --description--
以降のチャレンジでは、Redux で重要な状態の不変性の原則を強制するいくつかの方法について説明します。 イミュータブルな state とは、state を直接変更せず、代わりに state の新しいコピーを返すことです。
Redux アプリの state のスナップショットを時間を追って取得すると、`state 1``state 2``state 3``state 4``...` のようなものが取得されます。各 state は最後の状態と似ている可能性がありますが、それぞれ別個のデータです。 この不変性こそが、皆さんが耳にしたことがあるかもしれないタイムトラベルデバッグのような機能を実現します。
Redux では、ストアやレデューサーで状態の不変性を積極的には強制していません。その責任はプログラマーが担います。 幸い、JavaScript (特にES6) では状態の不変性の強制に使用できる便利なツールがいくつか提供されており、`string` でも、`number` でも、 `array` でも、または `object` でも利用できます。 ちなみに文字列と数値はプリミティブな値であり、本質的に不変です。 別の言い方をすれば、3 は常に 3 です。 数値 3 の値を変更することはできません。 しかし、`array``object` はミュータブルです。 実際には、state はおそらく `array` または `object` で構成されます。これは、数多くの種類の情報を表す便利なデータ構造だからです。
# --instructions--
コードエディターに、To-do アイテムを管理する `store``reducer` があります。 リデューサーの `ADD_TO_DO` case を書き換えて、state の末尾に新しい To-do を追加してください。 標準的な JavaScript または ES6 を使用して、いくつかの方法でこのことを実行できます。 `action.todo` からのアイテムが末尾に追加された新しい配列を返す方法を見つけてください。
# --hints--
Redux ストアが存在し、コードエディターで state を `todos` 配列に初期化する必要があります。
```js
assert(
(function () {
const todos = [
'Go to the store',
'Clean the house',
'Cook dinner',
'Learn to code'
];
const initialState = store.getState();
return (
Array.isArray(initialState) && initialState.join(',') === todos.join(',')
);
})()
);
```
`addToDo``immutableReducer` はどちらも関数である必要があります。
```js
assert(typeof addToDo === 'function' && typeof immutableReducer === 'function');
```
Redux ストアでのタイプ `ADD_TO_DO` のアクションのディスパッチで、`todo` アイテムを追加します。state をミューテートしてはいけません。
```js
assert(
(function () {
const initialState = store.getState();
const isFrozen = DeepFreeze(initialState);
store.dispatch(addToDo('__TEST__TO__DO__'));
const finalState = store.getState();
const expectedState = [
'Go to the store',
'Clean the house',
'Cook dinner',
'Learn to code',
'__TEST__TO__DO__'
];
return isFrozen && DeepEqual(finalState, expectedState);
})()
);
```
# --seed--
## --seed-contents--
```js
const ADD_TO_DO = 'ADD_TO_DO';
// A list of strings representing tasks to do:
const todos = [
'Go to the store',
'Clean the house',
'Cook dinner',
'Learn to code',
];
const immutableReducer = (state = todos, action) => {
switch(action.type) {
case ADD_TO_DO:
// Don't mutate state here or the tests will fail
return
default:
return state;
}
};
const addToDo = (todo) => {
return {
type: ADD_TO_DO,
todo
}
}
const store = Redux.createStore(immutableReducer);
```
# --solutions--
```js
const ADD_TO_DO = 'ADD_TO_DO';
const todos = [
'Go to the store',
'Clean the house',
'Cook dinner',
'Learn to code',
];
const immutableReducer = (state = todos, action) => {
switch(action.type) {
case ADD_TO_DO:
return state.concat(action.todo);
default:
return state;
}
};
const addToDo = (todo) => {
return {
type: ADD_TO_DO,
todo
}
}
const store = Redux.createStore(immutableReducer);
```

View File

@@ -0,0 +1,118 @@
---
id: 5a24c314108439a4d4036153
title: ストアリスナーを登録する
challengeType: 6
forumTopicId: 301446
dashedName: register-a-store-listener
---
# --description--
Redux `store` オブジェクトでアクセスできるもう一つのメソッドは `store.subscribe()` です。 このメソッドでは、ストアにリスナー関数をサブスクライブすることができます。リスナーは、ストアに対してアクションがディスパッチされるたびに呼び出されます。 このメソッドの簡単な用法の一つとして、アクションを受信してストアを更新するたびに、単にメッセージを記録する関数をストアにサブスクライブすることができます。
# --instructions--
ストアがアクションを受信するたびにグローバル変数 `count` を 1 増やすコールバック関数を記述し、この関数を `store.subscribe()` メソッドに渡してください。 アクションオブジェクトを直接渡すたびに、`store.dispatch()` が 3 回連続して呼び出されることがわかります。 アクションがディスパッチされる合間のコンソール出力を表示して、更新が行われる様子を確認してください。
# --hints--
ストアでの `ADD` アクションのディスパッチで、state を `1` 増やします。
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch({ type: 'ADD' });
const newState = store.getState();
return newState === initialState + 1;
})()
);
```
`store.subscribe` を使用してストアにリスナー関数をサブスクライブします。
```js
(getUserInput) => assert(getUserInput('index').match(/store\s*\.\s*subscribe\(/gm));
```
`store.subscribe` は関数を受け取ります。
```js
(getUserInput) => assert(getUserInput('index').match(/(\s*function\s*)|(\s*\(\s*\)\s*=>)/gm))
```
`store.subscribe` へのコールバックでも、ストアが更新されたときにグローバルの `count` 変数を 1 増やします。
```js
assert(store.getState() === count);
```
# --seed--
## --before-user-code--
```js
count = 0;
```
## --seed-contents--
```js
const ADD = 'ADD';
const reducer = (state = 0, action) => {
switch(action.type) {
case ADD:
return state + 1;
default:
return state;
}
};
const store = Redux.createStore(reducer);
// Global count variable:
let count = 0;
// Change code below this line
// Change code above this line
store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);
```
# --solutions--
```js
const ADD = 'ADD';
const reducer = (state = 0, action) => {
switch(action.type) {
case ADD:
return state + 1;
default:
return state;
}
};
const store = Redux.createStore(reducer);
let count = 0;
// Change code below this line
store.subscribe( () =>
{
count++;
}
);
// Change code above this line
store.dispatch({type: ADD});
store.dispatch({type: ADD});
store.dispatch({type: ADD});
```

View File

@@ -0,0 +1,114 @@
---
id: 5a24c314108439a4d403615a
title: 配列からアイテムを削除する
challengeType: 6
forumTopicId: 301447
dashedName: remove-an-item-from-an-array
---
# --description--
配列からアイテムを削除する練習をしましょう。 ここでもスプレッド演算子を使用できます。 他にも `slice()``concat()` といった便利な JavaScript メソッドがあります。
# --instructions--
アイテムのインデックスに基づいて配列からアイテムを削除するように、リデューサーとアクションクリエイターを変更しました。 レデューサーの記述を完成させて、指定されたインデックスのアイテムが削除された、新しい state 配列を返すようにしてください。
# --hints--
Redux ストアが存在し、state を `[0,1,2,3,4,5]` にして初期化する必要があります。
```js
assert(
(function () {
const initialState = store.getState();
return (
Array.isArray(initialState) === true &&
DeepEqual(initialState, [0, 1, 2, 3, 4, 5])
);
})()
);
```
`removeItem``immutableReducer` はどちらも関数である必要があります。
```js
assert(
typeof removeItem === 'function' && typeof immutableReducer === 'function'
);
```
`removeItem` アクションクリエイターのディスパッチで、state からアイテムを削除します。state をミューテートしてはいけません。
```js
assert(
(function () {
const initialState = store.getState();
const isFrozen = DeepFreeze(initialState);
store.dispatch(removeItem(3));
const state_1 = store.getState();
store.dispatch(removeItem(2));
const state_2 = store.getState();
store.dispatch(removeItem(0));
store.dispatch(removeItem(0));
store.dispatch(removeItem(0));
const state_3 = store.getState();
return (
isFrozen &&
DeepEqual(state_1, [0, 1, 2, 4, 5]) &&
DeepEqual(state_2, [0, 1, 4, 5]) &&
DeepEqual(state_3, [5])
);
})()
);
```
# --seed--
## --seed-contents--
```js
const immutableReducer = (state = [0,1,2,3,4,5], action) => {
switch(action.type) {
case 'REMOVE_ITEM':
// Don't mutate state here or the tests will fail
return
default:
return state;
}
};
const removeItem = (index) => {
return {
type: 'REMOVE_ITEM',
index
}
}
const store = Redux.createStore(immutableReducer);
```
# --solutions--
```js
const immutableReducer = (state = [0,1,2,3,4,5], action) => {
switch(action.type) {
case 'REMOVE_ITEM':
return [
...state.slice(0, action.index),
...state.slice(action.index + 1)
];
default:
return state;
}
};
const removeItem = (index) => {
return {
type: 'REMOVE_ITEM',
index
}
}
const store = Redux.createStore(immutableReducer);
```

View File

@@ -0,0 +1,107 @@
---
id: 5a24c314108439a4d4036155
title: ストアにアクションデータを送信する
challengeType: 6
forumTopicId: 301448
dashedName: send-action-data-to-the-store
---
# --description--
ここまで、Redux ストアにアクションをディスパッチする方法を説明しました。しかしこれまでのところ、これらのアクションには `type` 以外の情報は何も含まれていません。 アクションと一緒に特定のデータを送信することもできます。 実際、この操作はよく使用されます。その理由は、アクションは通常、ユーザーとの何らかのやり取りから発生し、それらと一緒に何らかのデータが送られる場合が多いからです。 多くの場合、Redux ストアではそうしたデータについて知る必要があります。
# --instructions--
コードエディターで、基本的な `notesReducer()``addNoteText()` アクションクリエイターが定義されています。 `action` オブジェクトを返すように `addNoteText()` 関数の本体を完成させてください。 オブジェクトには、`ADD_NOTE` という値を持つ `type` プロパティを含める必要があり、アクションクリエイターに渡される `note` データが設定された `text` プロパティも含める必要があります。 アクションクリエイターを呼び出すときには、オブジェクトに対してアクセスできる具体的なノート情報を渡します。
次に、`notesReducer()``switch` ステートメントの記述を完成させてください。 `addNoteText()` のアクションを処理する case を追加する必要があります。 この case は、タイプ `ADD_NOTE` のアクションが存在する場合にトリガーする必要があり、`action` を受信したときに新しい `state` として `text` プロパティを返す必要があります。
アクションはコードの最後でディスパッチされます。 記述を終えたら、コードを実行してコンソールを確認してください。 以上のコードだけで、アクション固有のデータをストアに送信して、ストアの `state` を更新するときに使用することができます。
# --hints--
アクションクリエイター `addNoteText` から、`type``text` というキーを持つオブジェクトを返します。
```js
assert(
(function () {
const addNoteFn = addNoteText('__TEST__NOTE');
return addNoteFn.type === ADD_NOTE && addNoteFn.text === '__TEST__NOTE';
})()
);
```
`addNoteText` アクションクリエイターによるタイプ `ADD_NOTE` のアクションのディスパッチで、`state` を、アクションクリエイターに渡された文字列に更新します。
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(addNoteText('__TEST__NOTE'));
const newState = store.getState();
return initialState !== newState && newState === '__TEST__NOTE';
})()
);
```
# --seed--
## --seed-contents--
```js
const ADD_NOTE = 'ADD_NOTE';
const notesReducer = (state = 'Initial State', action) => {
switch(action.type) {
// Change code below this line
// Change code above this line
default:
return state;
}
};
const addNoteText = (note) => {
// Change code below this line
// Change code above this line
};
const store = Redux.createStore(notesReducer);
console.log(store.getState());
store.dispatch(addNoteText('Hello!'));
console.log(store.getState());
```
# --solutions--
```js
const ADD_NOTE = 'ADD_NOTE';
const notesReducer = (state = 'Initial State', action) => {
switch(action.type) {
// Change code below this line
case ADD_NOTE:
return action.text;
// Change code above this line
default:
return state;
}
};
const addNoteText = (note) => {
// Change code below this line
return {
type: ADD_NOTE,
text: note
}
// Change code above this line
};
const store = Redux.createStore(notesReducer);
console.log(store.getState());
store.dispatch(addNoteText('Hello Redux!'));
console.log(store.getState());
```

View File

@@ -0,0 +1,152 @@
---
id: 5a24c314108439a4d4036151
title: Switch ステートメントを使用して複数のアクションを処理する
challengeType: 6
forumTopicId: 301449
dashedName: use-a-switch-statement-to-handle-multiple-actions
---
# --description--
Redux ストアに、複数のアクションタイプを処理する方法を指示することができます。 たとえば、Redux ストアでユーザー認証を管理していて、 ユーザーがログインしているときとログアウトしているときの状態を表現する必要があるとします。 それには、プロパティ `authenticated` を持つ単一の state オブジェクトを使用して状態を表現します。 また、アクションオブジェクトと一緒に、ユーザーログインとユーザーログアウトに対応したアクションを作成するアクションクリエイターも必要です。
# --instructions--
コードエディターに、ストア、アクション、そしてアクションクリエイターが用意されています。 複数の認証アクションを処理するように `reducer` 関数を記述してください。 `reducer` で JavaScript の `switch` ステートメントを使用して、さまざまなアクションイベントに応答してください。 これは Redux のレデューサーを記述する際の標準的なパターンになります。 switch ステートメントでは、`action.type` に応じて処理を切り替え、適切な認証状態を返してください。
**注:** ここでは状態の不変性については心配しないでください。なぜなら、この例では小さく単純だからです。 アクションごとに、たとえば `{authenticated: true}` のように新しいオブジェクトを返すことができます。 また、switch ステートメントで `default` の case を忘れずに記述し、現在の `state` を返してください。 これが重要な理由は、アプリに複数のレデューサーを記述すると、たとえアクションがそのレデューサーに関連したものでなくても、アクションがディスパッチされるといつでもそれらのレデューサーがすべて実行されるからです。 そのような場合は、必ず現在の `state` を返してください。
# --hints--
関数 `loginUser` の呼び出しで、type プロパティを文字列 `LOGIN` に設定したオブジェクトを返します。
```js
assert(loginUser().type === 'LOGIN');
```
関数 `logoutUser` の呼び出しで、type プロパティを文字列 `LOGOUT` に設定したオブジェクトを返します。
```js
assert(logoutUser().type === 'LOGOUT');
```
オブジェクトの `authenticated` プロパティを `false` に設定してストアを初期化します。
```js
assert(store.getState().authenticated === false);
```
`loginUser` のディスパッチで、ストアの state の `authenticated` プロパティを `true` に更新します。
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(loginUser());
const afterLogin = store.getState();
return (
initialState.authenticated === false && afterLogin.authenticated === true
);
})()
);
```
`logoutUser` のディスパッチで、ストアの state の `authenticated` プロパティを `false` に更新します。
```js
assert(
(function () {
store.dispatch(loginUser());
const loggedIn = store.getState();
store.dispatch(logoutUser());
const afterLogout = store.getState();
return (
loggedIn.authenticated === true && afterLogout.authenticated === false
);
})()
);
```
`authReducer` 関数で、`switch` ステートメントを使用して複数のアクションタイプを処理します。
```js
(getUserInput) =>
assert(
getUserInput('index').toString().includes('switch') &&
getUserInput('index').toString().includes('case') &&
getUserInput('index').toString().includes('default')
);
```
# --seed--
## --seed-contents--
```js
const defaultState = {
authenticated: false
};
const authReducer = (state = defaultState, action) => {
// Change code below this line
// Change code above this line
};
const store = Redux.createStore(authReducer);
const loginUser = () => {
return {
type: 'LOGIN'
}
};
const logoutUser = () => {
return {
type: 'LOGOUT'
}
};
```
# --solutions--
```js
const defaultState = {
authenticated: false
};
const authReducer = (state = defaultState, action) => {
switch (action.type) {
case 'LOGIN':
return {
authenticated: true
}
case 'LOGOUT':
return {
authenticated: false
}
default:
return state;
}
};
const store = Redux.createStore(authReducer);
const loginUser = () => {
return {
type: 'LOGIN'
}
};
const logoutUser = () => {
return {
type: 'LOGOUT'
}
};
```

View File

@@ -0,0 +1,205 @@
---
id: 5a24c314108439a4d4036152
title: アクションタイプに const を使用する
challengeType: 6
forumTopicId: 301450
dashedName: use-const-for-action-types
---
# --description--
Redux では、アクションタイプを読み取り専用の定数として割り当て、必要なすべての場所でそれらの定数を参照するという作業がよく行われます。 作業中のコードをリファクタリングして、アクションタイプを `const` 宣言として記述することができます。
# --instructions--
`LOGIN` および `LOGOUT``const` 値として宣言し、それぞれに文字列 `'LOGIN'` および `'LOGOUT'` を割り当ててください。 次に、`authReducer()` とアクションクリエイターを編集して、文字列値ではなくそれらの定数を参照するようにしてください。
**注:** 一般に、定数はすべて大文字で記述することが慣習になっていて、Redux でも標準的になっています。
# --hints--
関数 `loginUser` の呼び出しで、`type` プロパティを文字列 `LOGIN` に設定したオブジェクトを返します。
```js
assert(loginUser().type === 'LOGIN');
```
関数 `logoutUser` の呼び出しで、`type` プロパティを文字列 `LOGOUT` に設定したオブジェクトを返します。
```js
assert(logoutUser().type === 'LOGOUT');
```
オブジェクトのプロパティ `login``false` に設定してストアを初期化します。
```js
assert(store.getState().authenticated === false);
```
`loginUser` のディスパッチで、ストアの state の `login` プロパティを `true` に更新します。
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(loginUser());
const afterLogin = store.getState();
return (
initialState.authenticated === false && afterLogin.authenticated === true
);
})()
);
```
`logoutUser` のディスパッチで、ストアの state の `login` プロパティを `false` に更新します。
```js
assert(
(function () {
store.dispatch(loginUser());
const loggedIn = store.getState();
store.dispatch(logoutUser());
const afterLogout = store.getState();
return (
loggedIn.authenticated === true && afterLogout.authenticated === false
);
})()
);
```
`authReducer` 関数で、 switch ステートメントを使用して複数のアクションタイプを処理します。
```js
(getUserInput) =>
assert(
(function () {
return (
typeof authReducer === 'function' &&
getUserInput('index').toString().includes('switch') &&
getUserInput('index').toString().includes('case') &&
getUserInput('index').toString().includes('default')
);
})()
);
```
`LOGIN` および `LOGOUT``const` 値として宣言し、`LOGIN` および `LOGOUT` という文字列を割り当てます。
```js
const noWhiteSpace = __helpers.removeWhiteSpace(code);
assert(
(/constLOGIN=(['"`])LOGIN\1/.test(noWhiteSpace) &&
/constLOGOUT=(['"`])LOGOUT\1/.test(noWhiteSpace)) ||
/const(LOGIN|LOGOUT)=(['"`])\1\2,(?!\1)(LOGIN|LOGOUT)=(['"`])\3\4/.test(noWhiteSpace)
);
```
アクションクリエイターとレデューサーで、`LOGIN` 定数と `LOGOUT` 定数を参照します。
```js
(getUserInput) =>
assert(
(function () {
const noWhiteSpace = __helpers.removeWhiteSpace(
getUserInput('index').toString()
);
return (
noWhiteSpace.includes('caseLOGIN:') &&
noWhiteSpace.includes('caseLOGOUT:') &&
noWhiteSpace.includes('type:LOGIN') &&
noWhiteSpace.includes('type:LOGOUT')
);
})()
);
```
# --seed--
## --seed-contents--
```js
const defaultState = {
authenticated: false
};
const authReducer = (state = defaultState, action) => {
switch (action.type) {
case 'LOGIN':
return {
authenticated: true
}
case 'LOGOUT':
return {
authenticated: false
}
default:
return state;
}
};
const store = Redux.createStore(authReducer);
const loginUser = () => {
return {
type: 'LOGIN'
}
};
const logoutUser = () => {
return {
type: 'LOGOUT'
}
};
```
# --solutions--
```js
const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';
const defaultState = {
authenticated: false
};
const authReducer = (state = defaultState, action) => {
switch (action.type) {
case LOGIN:
return {
authenticated: true
}
case LOGOUT:
return {
authenticated: false
}
default:
return state;
}
};
const store = Redux.createStore(authReducer);
const loginUser = () => {
return {
type: LOGIN
}
};
const logoutUser = () => {
return {
type: LOGOUT
}
};
```

View File

@@ -0,0 +1,170 @@
---
id: 5a24c314108439a4d4036156
title: ミドルウェアを使用して非同期アクションを処理する
challengeType: 6
forumTopicId: 301451
dashedName: use-middleware-to-handle-asynchronous-actions
---
# --description--
ここまでのチャレンジでは、非同期アクションについて説明を避けてきましたが、これはウェブ開発において避けることのできないテーマです。 Redux アプリでも、ある特定の時点で非同期のエンドポイントを呼び出すことが必要になります。このような要求はどう処理すればよいのでしょうか? Redux では、この目的のために設計されたミドルウェアが用意されており、Redux Thunk ミドルウェアと呼ばれています。 ここでは、Redux でこのミドルウェアを使用する方法を簡単に説明します。
Redux Thunk ミドルウェアをインクルードするには、ミドルウェアを `Redux.applyMiddleware()` への引数として渡します。 次に、このステートメントを `createStore()` 関数への 2 つ目のオプションパラメーターとして渡します。 エディターの一番下のコードでこれを確認してください。 その次に、非同期アクションを作成するため、アクションクリエイターで `dispatch` を引数に取る関数を返します。 この関数の中で、アクションをディスパッチしたり、非同期要求を実行したりできます。
この例では `setTimeout()` を呼び出して非同期要求をシミュレートしています。 通常は非同期処理を開始する前にアクションをディスパッチして、何らかのデータが要求されていることをアプリケーションの state に知らせます (この state では、たとえば読み込み中を示すアイコンを表示したりできます)。 その後、データを受信した時点で別のアクションをディスパッチします。このアクションは、アクションが完了したという情報と一緒に、データをペイロードとして運びます。
この特別なアクションクリエイターには `dispatch` をパラメーターとして渡していることを覚えておいてください。 これを使用してアクションをディスパッチします。ディスパッチに直接アクションを渡すだけで、残りの処理はミドルウェアが行ってくれます。
# --instructions--
`handleAsync()` アクションクリエイターに両方のディスパッチを記述してください。 `requestingData()``setTimeout()` (API 呼び出しのシミュレーション) の前でディスパッチしてください。 次に、データを受信 (する真似を) した後で、`receivedData()` アクションをディスパッチし、このデータを渡してください。 これで、Redux での非同期アクションの処理方法がわかりました。 他はすべて以前と同じように動作します。
# --hints--
`requestingData` アクションクリエイターから、タイプが `REQUESTING_DATA` の値に等しいオブジェクトを返します。
```js
assert(requestingData().type === REQUESTING_DATA);
```
`receivedData` アクションクリエイターから、タイプが `RECEIVED_DATA` の値に等しいオブジェクトを返します。
```js
assert(receivedData('data').type === RECEIVED_DATA);
```
`asyncDataReducer` は関数である必要があります。
```js
assert(typeof asyncDataReducer === 'function');
```
`requestingData` アクションクリエイターのディスパッチで、ストアのフェッチの `state` プロパティを `true` に更新します。
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(requestingData());
const reqState = store.getState();
return initialState.fetching === false && reqState.fetching === true;
})()
);
```
`handleAsync` のディスパッチで、データ要求アクションをディスパッチし、遅延の後に、受信したデータアクションをディスパッチします。
```js
assert(
(function () {
const noWhiteSpace = __helpers.removeWhiteSpace(handleAsync.toString());
return (
noWhiteSpace.includes('dispatch(requestingData())') === true &&
noWhiteSpace.includes('dispatch(receivedData(data))') === true
);
})()
);
```
# --seed--
## --seed-contents--
```js
const REQUESTING_DATA = 'REQUESTING_DATA'
const RECEIVED_DATA = 'RECEIVED_DATA'
const requestingData = () => { return {type: REQUESTING_DATA} }
const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }
const handleAsync = () => {
return function(dispatch) {
// Dispatch request action here
setTimeout(function() {
let data = {
users: ['Jeff', 'William', 'Alice']
}
// Dispatch received data action here
}, 2500);
}
};
const defaultState = {
fetching: false,
users: []
};
const asyncDataReducer = (state = defaultState, action) => {
switch(action.type) {
case REQUESTING_DATA:
return {
fetching: true,
users: []
}
case RECEIVED_DATA:
return {
fetching: false,
users: action.users
}
default:
return state;
}
};
const store = Redux.createStore(
asyncDataReducer,
Redux.applyMiddleware(ReduxThunk.default)
);
```
# --solutions--
```js
const REQUESTING_DATA = 'REQUESTING_DATA'
const RECEIVED_DATA = 'RECEIVED_DATA'
const requestingData = () => { return {type: REQUESTING_DATA} }
const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }
const handleAsync = () => {
return function(dispatch) {
dispatch(requestingData());
setTimeout(function() {
let data = {
users: ['Jeff', 'William', 'Alice']
}
dispatch(receivedData(data));
}, 2500);
}
};
const defaultState = {
fetching: false,
users: []
};
const asyncDataReducer = (state = defaultState, action) => {
switch(action.type) {
case REQUESTING_DATA:
return {
fetching: true,
users: []
}
case RECEIVED_DATA:
return {
fetching: false,
users: action.users
}
default:
return state;
}
};
const store = Redux.createStore(
asyncDataReducer,
Redux.applyMiddleware(ReduxThunk.default)
);
```

View File

@@ -0,0 +1,114 @@
---
id: 5a24c314108439a4d4036159
title: 配列でスプレッド演算子を使用する
challengeType: 6
forumTopicId: 301452
dashedName: use-the-spread-operator-on-arrays
---
# --description--
ES6 以降において、Redux で状態の不変性を強制するための解決策の一つとなるのが、スプレッド演算子 `...` です。 スプレッド演算子にはさまざまな応用例があります。前のチャレンジで、既存の配列から新しい配列を生成することを説明しましたが、スプレッド演算子の一つはそうした操作に適しています。 この構文は比較的新しいものですが、一般的に使用されています。 たとえば、配列 `myArray` があり、次のように記述するとします。
```js
let newArray = [...myArray];
```
すると、`newArray``myArray` のクローンになります。 どちらの配列もまだメモリ内に個別に存在します。 `newArray.push(5)` のようなミューテーションを実行した場合でも、`myArray` は変更されません。 `...` によって、`myArray` 内の値が新しい配列に効果的に*展開 (スプレッド)* されます。 配列を複製して、新しい配列にさらに値を追加する場合は、たとえば `[...myArray, 'new value']` と記述できます。 この場合は、`myArray` 内の値と、最後の値として `new value` という文字列で構成された、新しい配列を返します。 スプレッド構文はこうした配列の構成で何度も使用できます。しかし重要なのは、配列のシャローコピーしか作成しないことです。 つまり、一次元配列に対するイミュータブルな配列操作しか提供しません。
# --instructions--
スプレッド演算子を使用して、To-do が追加されたときに state の新しいコピーを返してください。
# --hints--
Redux ストアが存在し、state を `["Do not mutate state!"]` にして初期化する必要があります。
```js
assert(
(function () {
const initialState = store.getState();
return (
Array.isArray(initialState) === true &&
initialState[0] === 'Do not mutate state!'
);
})()
);
```
`addToDo``immutableReducer` はどちらも関数である必要があります。
```js
assert(typeof addToDo === 'function' && typeof immutableReducer === 'function');
```
Redux ストアでのタイプ `ADD_TO_DO` のアクションのディスパッチで、`todo` アイテムを追加します。state をミューテートしてはいけません。
```js
assert(
(function () {
const initialState = store.getState();
const isFrozen = DeepFreeze(initialState);
store.dispatch(addToDo('__TEST__TO__DO__'));
const finalState = store.getState();
const expectedState = ['Do not mutate state!', '__TEST__TO__DO__'];
return isFrozen && DeepEqual(finalState, expectedState);
})()
);
```
スプレッド演算子を使用して新しい state を返します。
```js
(getUserInput) => assert(getUserInput('index').includes('...state'));
```
# --seed--
## --seed-contents--
```js
const immutableReducer = (state = ['Do not mutate state!'], action) => {
switch(action.type) {
case 'ADD_TO_DO':
// Don't mutate state here or the tests will fail
return
default:
return state;
}
};
const addToDo = (todo) => {
return {
type: 'ADD_TO_DO',
todo
}
}
const store = Redux.createStore(immutableReducer);
```
# --solutions--
```js
const immutableReducer = (state = ['Do not mutate state!'], action) => {
switch(action.type) {
case 'ADD_TO_DO':
return [
...state,
action.todo
];
default:
return state;
}
};
const addToDo = (todo) => {
return {
type: 'ADD_TO_DO',
todo
}
}
const store = Redux.createStore(immutableReducer);
```

View File

@@ -0,0 +1,116 @@
---
id: 5a24c314108439a4d4036157
title: Redux を使用してカウンターを記述する
challengeType: 6
forumTopicId: 301453
dashedName: write-a-counter-with-redux
---
# --description--
以上で Redux の中心的な原則のすべてを説明しました。 アクションとアクションクリエイターの作成方法、Redux ストアの作成方法、ストアに対するアクションのディスパッチ方法、純粋なレデューサーによる state の更新の設計方法について説明しました。 さらに、レデューサーコンポジションによる複雑な状態管理の方法、非同期アクションの処理方法についても説明しました。 ここで紹介した例はごく単純なものですが、その概念は Redux の中心的な原則となっています。 以上のことを十分に理解すれば、Redux アプリを自分で構築する作業に取り掛かることができます。 以降のチャレンジでは `state` の不変性に関する詳細について触れますが、まずはこれまでに学んだすべてのことをここでおさらいしておきましょう。
# --instructions--
このレッスンでは、Redux を使用したシンプルなカウンターをゼロから実装します。 基本的な部分はコードエディターに用意してありますが、詳細は皆さんが記述する必要があります。 用意された名前を使用して、`incAction``decAction` というアクションクリエイター、`counterReducer()`、および `INCREMENT``DECREMENT` というアクションタイプを定義し、最後に Redux `store` を定義してください。 完成すると、`INCREMENT` または `DECREMENT` アクションをディスパッチして、`store` に保持されている state を 1 増やすまたは 1 減らすことができるようになります。 初めての Redux アプリを作成できるようがんばってください!
# --hints--
アクションクリエイター `incAction` から、`type``INCREMENT` という値に等しいアクションオブジェクトを返します。
```js
assert(incAction().type === INCREMENT);
```
アクションクリエイター `decAction` から、`type``DECREMENT` という値に等しいアクションオブジェクトを返します。
```js
assert(decAction().type === DECREMENT);
```
Redux ストアを、`state` を 0 として初期化します。
```js
assert(store.getState() === 0);
```
Redux ストアでの `incAction` のディスパッチで、`state` を 1 だけ増やします。
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(incAction());
const incState = store.getState();
return initialState + 1 === incState;
})()
);
```
Redux ストアでの `decAction` のディスパッチで、`state` を 1 だけ減らします。
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(decAction());
const decState = store.getState();
return initialState - 1 === decState;
})()
);
```
`counterReducer` は関数である必要があります。
```js
assert(typeof counterReducer === 'function');
```
# --seed--
## --seed-contents--
```js
const INCREMENT = null; // Define a constant for increment action types
const DECREMENT = null; // Define a constant for decrement action types
const counterReducer = null; // Define the counter reducer which will increment or decrement the state based on the action it receives
const incAction = null; // Define an action creator for incrementing
const decAction = null; // Define an action creator for decrementing
const store = null; // Define the Redux store here, passing in your reducers
```
# --solutions--
```js
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const counterReducer = (state = 0, action) => {
switch(action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
};
const incAction = () => {
return {
type: INCREMENT
}
};
const decAction = () => {
return {
type: DECREMENT
}
};
const store = Redux.createStore(counterReducer);
```