fix(i18n): update Chinese translation of redux (#38825)
This commit is contained in:
@ -3,28 +3,43 @@ id: 5a24c314108439a4d4036154
|
||||
title: Combine Multiple Reducers
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 结合多个减速器
|
||||
forumTopicId: 301436
|
||||
localeTitle: 组合多个 Reduces
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">当您的应用程序的状态开始变得更加复杂时,可能很容易将状态分成多个部分。相反,请记住Redux的第一个原则:所有应用程序状态都保存在商店中的单个状态对象中。因此,Redux提供减速器组合作为复杂状态模型的解决方案。您可以定义多个reducers来处理应用程序状态的不同部分,然后将这些reducer组合成一个根reducer。然后将根reducer传递给Redux <code>createStore()</code>方法。为了让我们将多个reducer组合在一起,Redux提供了<code>combineReducers()</code>方法。此方法接受一个对象作为参数,您可以在其中定义将键与特定reducer函数关联的属性。 Redux将使用您为密钥指定的名称作为相关状态的名称。通常,当每个应用程序状态以某种方式不同或唯一时,为每个应用程序状态创建一个减速器是一个好习惯。例如,在具有用户身份验证的笔记记录应用程序中,一个reducer可以处理身份验证,而另一个reducer处理用户正在提交的文本和备注。对于这样的应用程序,我们可能会像这样编写<code>combineReducers()</code>方法: <blockquote> const rootReducer = Redux.combineReducers({ <br> auth:authenticationReducer, <br>笔记:notesReducer <br> }); </blockquote>现在,关键<code>notes</code>将包含与我们的注释相关联的所有状态,并由<code>notesReducer</code>处理。这就是如何组合多个reducers来管理更复杂的应用程序状态。在此示例中,Redux存储中保存的状态将是包含<code>auth</code>和<code>notes</code>属性的单个对象。 </section>
|
||||
<section id='description'>
|
||||
当你应用程序的状态开始变得越来越复杂时,将状态划分为多个部分可能是个更好的选择。相反,请记住 Redux 的第一个原则:所有应用程序状态都保存在 store 中的一个简单的 state 对象中。因此,Redux 提供 reducer 组合作为复杂状态模型的解决方案。定义多个 reducer 来处理应用程序状态的不同部分,然后将这些 reducer 组合成一个 root reducer。然后将 root reducer 传递给 Redux <code>createStore()</code>方法。
|
||||
为了让我们将可以将多个 reducer 组合在一起,Redux 提供了<code>combineReducers()</code>方法。该方法接受一个对象作为参数,在该参数中定义一个将键与特定 reducer 函数关联的属性。Redux 将使用你给的键值作为关联状态的名称。
|
||||
通常情况下,当它们在某种程度上是独一无二的,为每个应用程序的 state 创建一个减少器是一个很好的做法。例如,在一个带有用户身份验证的记笔记应用程序中,一个 reducer 可以处理身份验证而另一个处理用户提交的文本和注释。对于这样的应用程序,我们可能会编写<code>combineReducers()</code>方法,如下所示:
|
||||
|
||||
```js
|
||||
const rootReducer = Redux.combineReducers({
|
||||
auth: authenticationReducer,
|
||||
notes: notesReducer
|
||||
});
|
||||
```
|
||||
|
||||
现在,<code>notes</code>键将包含与我们的注释相关联的所有状态,并由我们的<code>notesReducer</code>处理。这就是如何组合多个 reducer 来管理更复杂的应用程序状态,在此示例中,Redux store 中保存的状态将是一个包含<code>auth</code>和<code>notes</code>属性的简单对象。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">代码编辑器中提供了<code>authReducer()</code> <code>counterReducer()</code>和<code>authReducer()</code>函数以及Redux存储。使用<code>Redux.combineReducers()</code>方法完成<code>rootReducer()</code>函数的<code>Redux.combineReducers()</code> 。指定<code>counterReducer</code>一键叫<code>count</code>和<code>authReducer</code>一个叫关键<code>auth</code> 。 </section>
|
||||
<section id='instructions'>
|
||||
代码编辑器中提供了<code>counterReducer()</code>和<code>authReducer()</code>函数以及 Redux store。使用<code>Redux.combineReducers()</code>方法编写完成<code>rootReducer()</code>函数。将<code>counterReducer</code>分配给一个叫做<code>count</code>的键,将<code>authReducer</code>分配给一个叫做<code>auth</code>的键。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: <code>counterReducer</code>应该递增和递减<code>state</code> 。
|
||||
- text: <code>counterReducer</code>应该递增和递减<code>state</code>。
|
||||
testString: '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 })());'
|
||||
- text: <code>authReducer</code>应切换在<code>true</code>和<code>false</code>之间进行<code>authenticated</code>的<code>state</code> 。
|
||||
- text: <code>authenticated</code>的<code>state</code>值应该在<code>true</code>和<code>false</code>之间切换。
|
||||
testString: '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 })());'
|
||||
- text: 存储<code>state</code>应该有两个键: <code>count</code> ,它包含一个数字, <code>auth</code> ,它包含一个对象。 <code>auth</code>对象应具有<code>authenticated</code>属性,该属性包含布尔值。
|
||||
- text: store <code>state</code>应该有两个 key:一个是<code>count</code>,它包含一个数字。另一个<code>auth</code>,它包含一个对象。<code>auth</code>对象应该具有<code>authenticated</code>的属性,该属性的值应该为布尔值。
|
||||
testString: "assert((function() { const state = store.getState(); return typeof state.auth === 'object' && typeof state.auth.authenticated === 'boolean' && typeof state.count === 'number' })());"
|
||||
- text: 该<code>rootReducer</code>应该是结合了功能<code>counterReducer</code>和<code>authReducer</code> 。
|
||||
- text: <code>rootReducer</code>应该是一个合并了<code>counterReducer</code>和<code>authReducer</code>的函数。
|
||||
testString: getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/\s/g,''); return typeof rootReducer === 'function' && noWhiteSpace.includes('Redux.combineReducers') })());
|
||||
|
||||
```
|
||||
@ -69,7 +84,7 @@ const authReducer = (state = {authenticated: false}, action) => {
|
||||
}
|
||||
};
|
||||
|
||||
const rootReducer = // define the root reducer here
|
||||
const rootReducer = // 在这里定义 root reducer
|
||||
|
||||
const store = Redux.createStore(rootReducer);
|
||||
|
||||
@ -84,8 +99,46 @@ const store = Redux.createStore(rootReducer);
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
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);
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,28 +3,34 @@ id: 5a24c314108439a4d403615b
|
||||
title: Copy an Object with Object.assign
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 使用Object.assign复制对象
|
||||
forumTopicId: 301437
|
||||
localeTitle: 使用 Object.assign 拷贝对象
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">最后几个挑战适用于数组,但是当状态是一个<code>object</code>时,有一些方法可以帮助强制执行状态不变性。处理对象的有用工具是<code>Object.assign()</code>实用程序。 <code>Object.assign()</code>接受目标对象和源对象,并将源对象中的属性映射到目标对象。任何匹配的属性都会被源对象中的属性覆盖。此行为通常用于通过将空对象作为第一个参数传递,然后传递要复制的对象来生成对象的浅副本。这是一个例子: <code>const newObject = Object.assign({}, obj1, obj2);</code>这将<code>newObject</code>创建为一个新<code>object</code> ,其中包含当前存在于<code>obj1</code>和<code>obj2</code>中的属性。 </section>
|
||||
<section id='description'>
|
||||
最后几个挑战适用于数组,但是当状态是<code>object</code>时,有一些方法可以帮助强制执行状态不变性。处理对象的一个方法是<code>Object.assign()</code>。<code> Object.assign()</code>获取目标对象和源对象,并将源对象中的属性映射到目标对象。任何匹配的属性都会被源对象中的属性覆盖。通常用于通过传递一个空对象作为第一个参数,然后是要用复制的对象来制作对象的浅表副本。这是一个例子:
|
||||
<code>const newObject = Object.assign({}, obj1, obj2);</code>
|
||||
这会创建<code>newObject</code>作为新的<code>object</code>,其中包含<code>obj1</code>和<code>obj2</code>中当前存在的属性。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions"> Redux状态和操作被修改为处理<code>state</code>的<code>object</code> 。编辑代码以返回类型为<code>ONLINE</code>操作的新<code>state</code>对象,该<code>status</code>属性将<code>status</code>属性设置为<code>online</code>字符串。尝试使用<code>Object.assign()</code>来完成挑战。 </section>
|
||||
<section id='instructions'>
|
||||
Redux 状态和 action 被修改为处理<code>state</code>的<code>对象</code>。编辑代码以返回一个新的<code>state</code>对象,用于类型的 action<code>ONLINE</code>,它将<code>status</code>属性设置为字符串<code>online</code>。尝试使用<code>Object.assign()</code>来完成挑战。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: Redux存储应该存在并使用等效于第1行声明的<code>defaultState</code>对象的状态进行初始化。
|
||||
- text: Redux store 应该存在并使用与第 1 行声明的<code>defaultState</code>对象相同的状态进行初始化。
|
||||
testString: 'assert((function() { const expectedState = { user: ''CamperBot'', status: ''offline'', friends: ''732,982'', community: ''freeCodeCamp'' }; const initialState = store.getState(); return DeepEqual(expectedState, initialState); })());'
|
||||
- text: <code>wakeUp</code>和<code>immutableReducer</code>都应该是函数。
|
||||
testString: assert(typeof wakeUp === 'function' && typeof immutableReducer === 'function');
|
||||
- text: 调度<code>ONLINE</code>类型的操作应该将<code>status</code>中的属性<code>status</code>更新为<code>online</code>并且不应该改变状态。
|
||||
- text: dispatch 一个类型为<code>ONLINE</code>的 action 应该将状态<code>status</code>更新为<code>online</code>,并且不应该改变状态。
|
||||
testString: '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); })());'
|
||||
- text: <code>Object.assign</code>应该用于返回新状态。
|
||||
- text: <code>Object.assign</code>应该被用于返回一个新状态。
|
||||
testString: getUserInput => assert(getUserInput('index').includes('Object.assign'));
|
||||
|
||||
```
|
||||
@ -47,7 +53,7 @@ const defaultState = {
|
||||
const immutableReducer = (state = defaultState, action) => {
|
||||
switch(action.type) {
|
||||
case 'ONLINE':
|
||||
// don't mutate state here or the tests will fail
|
||||
// 不要在这里改变 state 否则测试会失败。
|
||||
return
|
||||
default:
|
||||
return state;
|
||||
@ -61,7 +67,6 @@ const wakeUp = () => {
|
||||
};
|
||||
|
||||
const store = Redux.createStore(immutableReducer);
|
||||
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -73,8 +78,33 @@ const store = Redux.createStore(immutableReducer);
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
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);
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,24 +3,32 @@ id: 5a24c314108439a4d403614b
|
||||
title: Create a Redux Store
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 创建一个Redux商店
|
||||
forumTopicId: 301439
|
||||
localeTitle: 创建一个 Redux Store
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description"> Redux是一个状态管理框架,可以与许多不同的Web技术一起使用,包括React。在Redux中,有一个状态对象负责应用程序的整个状态。这意味着如果您有一个包含十个组件的React应用程序,并且每个组件都有自己的本地状态,则应用程序的整个状态将由Redux <code>store</code>的单个状态对象定义。这是学习Redux时理解的第一个重要原则:Redux商店是应用程序状态的唯一真实来源。这也意味着,只要您的应用程序的任何部分想要更新状态,它<strong>必须</strong>通过Redux商店执行此操作。单向数据流可以更轻松地跟踪应用程序中的状态管理。 </section>
|
||||
<section id='description'>
|
||||
Redux 是一个状态管理框架,可以与包括 React 在内的许多不同的 Web 技术一起使用。
|
||||
在 Redux 中,有一个状态对象负责应用程序的整个状态,这意味着如果你有一个包含十个组件且每个组件都有自己的本地状态的 React 项目,那么这个项目的整个状态将通过 Redux<code>store</code>被定义为单个状态对象,这是学习 Redux 时要理解的第一个重要原则:Redux store 是应用程序状态的唯一真实来源。
|
||||
这也意味着,如果你的应用程序想要更新状态,只能通过 Redux store 执行,单向数据流可以更轻松地对应用程序中的状态进行监测管理。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions"> Redux <code>store</code>是一个保存和管理应用程序<code>state</code>的对象。 Redux对象上有一个名为<code>createStore()</code>的方法,您可以使用该方法创建Redux <code>store</code> 。此方法将<code>reducer</code>函数作为必需参数。 <code>reducer</code>函数将在稍后的挑战中介绍,并且已在代码编辑器中为您定义。它只是将<code>state</code>作为参数并返回<code>state</code> 。声明一个<code>store</code>变量并将其赋值给<code>createStore()</code>方法,并将<code>reducer</code>作为参数传入。 <strong>注意:</strong>编辑器中的代码使用ES6默认参数语法初始化此状态以保存值<code>5</code> 。如果您不熟悉默认参数,可以参考<a target="_blank" href="https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/es6/set-default-parameters-for-your-functions">课程</a>中涵盖此主题的<a target="_blank" href="https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/es6/set-default-parameters-for-your-functions">ES6部分</a> 。 </section>
|
||||
<section id='instructions'>
|
||||
Redux <code>store</code>是一个保存和管理应用程序状态的<code>state</code>,你可以使用 Redux 对象中的<code>createStore()</code>来创建一个 redux<code>store</code>,此方法将<code>reducer</code>函数作为必需参数,<code>reducer</code>函数将在后面的挑战中介绍。该函数已在代码编辑器中为你定义,它只需将<code>state</code>作为参数并返回一个<code>state</code>即可。
|
||||
声明一个<code>store</code>变量并把它分配给<code>createStore()</code>方法,然后把<code>reducer</code>作为一个参数传入即可。
|
||||
注意: 编辑器中的代码使用 ES6 默认参数语法将 state 的值初始化为<code>5</code>, 如果你不熟悉默认参数,你可以参考<a target="_blank" href="http://beta.freecodecamp.com/en/challenges/es6/set-default-parameters-for-your-functions"> ES6 全部课程</a>,它里面涵盖了这个内容。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: redux商店存在。
|
||||
- text: redux store 应当存在。
|
||||
testString: assert(typeof store.getState === 'function');
|
||||
- text: redux商店的状态值为5。
|
||||
- text: redux store 的 state 的值为 5。
|
||||
testString: assert(store.getState()=== 5);
|
||||
|
||||
```
|
||||
@ -37,9 +45,11 @@ const reducer = (state = 5) => {
|
||||
return state;
|
||||
}
|
||||
|
||||
// Redux methods are available from a Redux object
|
||||
// For example: Redux.createStore()
|
||||
// Define the store here:
|
||||
// Redux 方法可以从 Redux 对象获得
|
||||
// 例如: Redux.createStore()
|
||||
// 在这里定义一个 store
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
@ -52,8 +62,17 @@ const reducer = (state = 5) => {
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
const reducer = (state = 5) => {
|
||||
return state;
|
||||
}
|
||||
|
||||
//Redux 方法可以从 Redux 对象获得
|
||||
// 例如: Redux.createStore()
|
||||
// 在这里定义一个 store:
|
||||
|
||||
const store = Redux.createStore(reducer);
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,24 +3,29 @@ id: 5a24c314108439a4d403614d
|
||||
title: Define a Redux Action
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 定义Redux动作
|
||||
forumTopicId: 301440
|
||||
localeTitle: 定义一个 Redux Action
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">由于Redux是一个状态管理框架,因此更新状态是其核心任务之一。在Redux中,所有状态更新都由调度操作触发。操作只是一个JavaScript对象,其中包含有关已发生的操作事件的信息。 Redux存储接收这些操作对象,然后相应地更新其状态。有时,Redux操作也会携带一些数据。例如,操作在用户登录后携带用户名。虽然数据是可选的,但操作必须带有<code>type</code>属性,该属性指定发生的操作的“类型”。将Redux操作视为信使,将有关应用程序中发生的事件的信息提供给Redux商店。然后,商店根据发生的操作开展更新状态的业务。 </section>
|
||||
<section id='description'>
|
||||
由于 Redux 是一个状态管理框架,因此更新状态是其核心任务之一。在 Redux 中,所有状态更新都由 dispatch action 触发,action 只是一个 JavaScript 对象,其中包含有关已发生的 action 事件的信息。Redux store 接收这些 action 对象,然后更新相应的状态。有时,Redux action 也会携带一些数据。例如,在用户登录后携带用户名,虽然数据是可选的,但 action 必须带有<code>type</code>属性,该属性表示此 action 的类型。
|
||||
我们可以将 Redux action 视为信使,将有关应用程序中发生的事件信息提供给 Redux store,然后 store 根据发生的 action 进行状态的更新。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">编写Redux操作就像声明具有type属性的对象一样简单。声明一个对象<code>action</code>并为其设置一个属性<code>type</code>设置为字符串<code>'LOGIN'</code> 。 </section>
|
||||
<section id='instructions'>
|
||||
编写 Redux action 就像声明具有 type 属性的对象一样简单,声明一个对象<code>action</code>并为它设置一个属性<code>type</code>,并将他的值设置成字符串<code>'LOGIN'</code>。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应该存在一个操作对象。
|
||||
- text: 应该有一个 action 对象。
|
||||
testString: assert((function() { return typeof action === 'object' })());
|
||||
- text: 该操作应具有值为<code>LOGIN</code>的键属性类型。
|
||||
- text: 该 action 的值应该为<code>LOGIN</code>。
|
||||
testString: assert((function() { return action.type === 'LOGIN' })());
|
||||
|
||||
```
|
||||
@ -33,7 +38,7 @@ tests:
|
||||
<div id='jsx-seed'>
|
||||
|
||||
```jsx
|
||||
// Define an action here:
|
||||
// 在此处定义 action
|
||||
|
||||
```
|
||||
|
||||
@ -46,8 +51,11 @@ tests:
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
const action = {
|
||||
type: 'LOGIN'
|
||||
}
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,15 +3,19 @@ id: 5a24c314108439a4d403614e
|
||||
title: Define an Action Creator
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 定义一个Action Creator
|
||||
forumTopicId: 301441
|
||||
localeTitle: 定义一个 Action Creator
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">创建操作后,下一步是将操作发送到Redux存储,以便它可以更新其状态。在Redux中,您可以定义动作创建器来完成此任务。动作创建者只是一个返回动作的JavaScript函数。换句话说,动作创建者创建表示动作事件的对象。 </section>
|
||||
<section id='description'>
|
||||
创建 action 后要将 action 发送到 Redux store,以便它可以更新其状态。在 Redux 中,你可以定义动作创建器来完成此任务,action creator 只是一个返回动作的 JavaScript 函数,换句话说,action creator 创建表示动作事件的对象。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">定义一个名为<code>actionCreator()</code>的函数,该函数在调用时返回<code>action</code>对象。 </section>
|
||||
<section id='instructions'>
|
||||
定义名为<code>actionCreator()</code>的函数,该函数在调用时返回<code>action</code>对象。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
@ -20,9 +24,9 @@ localeTitle: 定义一个Action Creator
|
||||
tests:
|
||||
- text: 函数<code>actionCreator</code>应该存在。
|
||||
testString: assert(typeof actionCreator === 'function');
|
||||
- text: 运行<code>actionCreator</code>函数应该返回操作对象。
|
||||
- text: 运行<code>actionCreator</code>函数应返回 action 对象。
|
||||
testString: assert(typeof action === 'object');
|
||||
- text: 返回的操作应具有值为<code>LOGIN</code>的键属性类型。
|
||||
- text: 返回的 action 应具有值为<code>LOGIN</code>的键值类型。
|
||||
testString: assert(action.type === 'LOGIN');
|
||||
|
||||
```
|
||||
@ -38,7 +42,8 @@ tests:
|
||||
const action = {
|
||||
type: 'LOGIN'
|
||||
}
|
||||
// Define an action creator here:
|
||||
// 在此处定义 action creator
|
||||
|
||||
|
||||
```
|
||||
|
||||
@ -51,8 +56,15 @@ const action = {
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
const action = {
|
||||
type: 'LOGIN'
|
||||
}
|
||||
// 在此处定义 action creator:
|
||||
const actionCreator = () => {
|
||||
return action;
|
||||
};
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,26 +3,37 @@ id: 5a24c314108439a4d403614f
|
||||
title: Dispatch an Action Event
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 派遣行动事件
|
||||
forumTopicId: 301442
|
||||
localeTitle: 分发 Action Event
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description"> <code>dispatch</code>方法是您用于将操作分派给Redux存储的方法。调用<code>store.dispatch()</code>并传递从操作创建者返回的值会将操作发送回商店。回想一下,动作创建者返回一个具有type属性的对象,该属性指定已发生的动作。然后,该方法将操作对象调度到Redux存储。根据之前的挑战示例,以下行是等效的,并且都调度<code>LOGIN</code>类型的操作: <blockquote> store.dispatch(actionCreator()); <br> store.dispatch({type:'LOGIN'}); </blockquote></section>
|
||||
<section id='description'>
|
||||
<code>dispatch</code>方法用于将 action 分派给 Redux store,调用<code>store.dispatch()</code>将从 action creator 返回的值发送回 store。
|
||||
action creator 返回一个具有 type 属性的对象,该属性指定已发生的 action,然后,该方法将 action 对象 dispatch 到 Redux store,根据之前的挑战示例,以下内容是等效的,并且都 dispatch 类型为<code>LOGIN</code>的 action:
|
||||
|
||||
```js
|
||||
store.dispatch(actionCreator());
|
||||
store.dispatch({ type: 'LOGIN' });
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">代码编辑器中的Redux存储具有初始化状态,该状态是包含当前设置为<code>false</code>的<code>login</code>属性的对象。还有一个名为<code>loginAction()</code>的动作创建器,它返回<code>LOGIN</code>类型的动作。通过调用<code>dispatch</code>方法将<code>LOGIN</code>操作发送到Redux存储,并传入<code>loginAction()</code>创建的操作。 </section>
|
||||
<section id='instructions'>
|
||||
代码编辑器中的 Redux store 具有初始化状态对象{login:'false'},还有一个名为<code>loginAction()</code>的 action creator,它返回类型为<code>LOGIN</code>的 action,然后通过调用<code>dispatch</code>方法将<code>LOGIN</code>的 action dispatch 给 Redux store,并传递<code>loginAction()</code>创建的 action。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 调用函数<code>loginAction</code>应该返回一个对象,其<code>type</code>属性设置为字符串<code>LOGIN</code> 。
|
||||
- text: '调用函数<code>loginAction</code>应该返回一个对象{type:"LOGIN"}。'
|
||||
testString: assert(loginAction().type === 'LOGIN');
|
||||
- text: 应使用属性<code>login</code>设置为<code>false</code>的对象初始化存储。
|
||||
- text: 'store 应该初始化一个对象 {login:false}。'
|
||||
testString: assert(store.getState().login === false);
|
||||
- text: <code>store.dispatch()</code>方法应该用于调度<code>LOGIN</code>类型的操作。
|
||||
- text: <code>store.dispatch()</code>方法应该被用于 dispatch 一个类型为<code>LOGIN</code>的 action。
|
||||
testString: "getUserInput => assert((function() { let noWhiteSpace = getUserInput('index').replace(/\\s/g,''); return noWhiteSpace.includes('store.dispatch(loginAction())') || noWhiteSpace.includes('store.dispatch({type: \\'LOGIN\\'})') === true })());"
|
||||
|
||||
```
|
||||
@ -45,7 +56,7 @@ const loginAction = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Dispatch the action here:
|
||||
// 在这里 dispatch action
|
||||
|
||||
```
|
||||
|
||||
@ -58,8 +69,20 @@ const loginAction = () => {
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
const store = Redux.createStore(
|
||||
(state = {login: false}) => state
|
||||
);
|
||||
|
||||
const loginAction = () => {
|
||||
return {
|
||||
type: 'LOGIN'
|
||||
}
|
||||
};
|
||||
|
||||
// Dispatch the action here:
|
||||
store.dispatch(loginAction());
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,24 +3,28 @@ id: 5a24c314108439a4d403614c
|
||||
title: Get State from the Redux Store
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 从Redux商店获取状态
|
||||
forumTopicId: 301443
|
||||
localeTitle: 从 Redux Store 获取状态
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description"> Redux存储对象提供了几种允许您与之交互的方法。例如,您可以使用<code>getState()</code>方法检索Redux存储对象中保存的当前<code>state</code> 。 </section>
|
||||
<section id='description'>
|
||||
Redux store 对象提供了几种允许你与之交互的方法,你可以使用<code>getState()</code>方法检索 Redux store 对象中保存的当前的<code>state</code>。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">上一个挑战中的代码在代码编辑器中更简洁地重写。使用<code>store.getState()</code>从<code>store</code>检索<code>state</code> ,并将其分配给新变量<code>currentState</code> 。 </section>
|
||||
<section id='instructions'>
|
||||
在代码编辑器中可以将上一个挑战中的代码更简洁地重写,在<code>store</code>中使用<code>store.getState()</code>检索<code>state</code>,并将其分配给新变量<code>currentState</code>。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 对于初始状态,redux存储的值应为5。
|
||||
- text: redux store 的 state 应该有一个初始值 5。
|
||||
testString: assert(store.getState()===5);
|
||||
- text: 应该存在一个变量<code>currentState</code> ,并且应该为其分配Redux存储的当前状态。
|
||||
- text: 应该存在一个变量<code>currentState</code>,并为其分配 Redux store 的当前状态。
|
||||
testString: getUserInput => assert(currentState === 5 && getUserInput('index').includes('store.getState()'));
|
||||
|
||||
```
|
||||
@ -37,7 +41,7 @@ const store = Redux.createStore(
|
||||
(state = 5) => state
|
||||
);
|
||||
|
||||
// change code below this line
|
||||
// 更改此行下方的代码
|
||||
|
||||
```
|
||||
|
||||
@ -50,8 +54,14 @@ const store = Redux.createStore(
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
const store = Redux.createStore(
|
||||
(state = 5) => state
|
||||
);
|
||||
|
||||
// 更改此行下方的代码
|
||||
const currentState = store.getState();
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,28 +3,33 @@ id: 5a24c314108439a4d4036150
|
||||
title: Handle an Action in the Store
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 处理商店中的操作
|
||||
forumTopicId: 301444
|
||||
localeTitle: 在 Store 里处理 Action
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">创建并分派操作后,Redux存储需要知道如何响应该操作。这是<code>reducer</code>功能的工作。 Redux中的Reducers负责响应操作而进行的状态修改。 <code>reducer</code>将<code>state</code>和<code>action</code>作为参数,并且它总是返回一个新<code>state</code> 。重要的是要看到这是减速器的<strong>唯一</strong>作用。它没有任何副作用 - 它从不调用API端点,它从来没有任何隐藏的意外。 reducer只是一个纯函数,它接受状态和动作,然后返回新状态。 Redux的另一个关键原则是<code>state</code>是只读的。换句话说, <code>reducer</code>函数必须<strong>始终</strong>返回一个新的<code>state</code>副本,而不是直接修改状态。 Redux不强制执行状态不变性,但是,您负责在reducer函数的代码中强制执行它。你将在以后的挑战中练习这一点。 </section>
|
||||
<section id='description'>
|
||||
在一个 action 被创建并 dispatch 之后,Redux store 需要知道如何响应该操作。这就是<code>reducer</code>函数存在的意义。Redux 中的 Reducers 负责响应 action 然后进行状态的修改。<code>reducer</code>将<code>state</code>和<code>action</code>作为参数,并且它总是返回一个新的<code>state</code>。我们要知道这是 reducer 的<strong>唯一</strong>的作用。它不应有任何其他的作用:比如它不应调用 API 接口,也不应存在任何潜在的副作用。reducer 只是一个接受状态和动作,然后返回新状态的纯函数。
|
||||
Redux 的另一个关键原则是<code>state</code>是只读的。换句话说,<code>reducer</code>函数必须<strong>始终</strong>返回一个新的<code>state</code>,并且永远不会直接修改状态。Redux 不强制改变状态,但是你需要在你的 reducer 函数的代码中强制执行它,你将在以后的挑战中练习这一点。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">代码编辑器具有前面的示例以及为您启动<code>reducer</code>功能。填写<code>reducer</code>函数的主体,这样如果它收到<code>'LOGIN'</code>类型的动作,它将返回一个<code>login</code>设置为<code>true</code>的状态对象。否则,它返回当前<code>state</code> 。请注意,当前<code>state</code>和调度的<code>action</code>将传递给reducer,因此您可以使用<code>action.type</code>直接访问操作的类型。 </section>
|
||||
<section id='instructions'>
|
||||
代码编辑器中具有前面的示例以及一个<code>reducer</code>函数。你需要完善<code>reducer</code>函数的内容,使得它如果收到类型为<code>'LOGIN'</code>的action,它将返回一个将<code>login</code>设置为<code>true</code>的 state 对象。否则,它就返回当前的<code>state</code>。请注意,当前<code>state</code>和dispatch的<code>action</code>将被传递给reducer,因此你可以使用<code>action.type</code>直接获取 action 的类型。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 调用函数<code>loginAction</code>应该返回一个对象,其type属性设置为字符串<code>LOGIN</code> 。
|
||||
- text: '调用<code>loginAction</code>函数应该返回一个对象 {type:"LOGIN"}。'
|
||||
testString: assert(loginAction().type === 'LOGIN');
|
||||
- text: 应使用属性<code>login</code>设置为<code>false</code>的对象初始化存储。
|
||||
- text: 'store 应该初始化一个对象 {login:false}。'
|
||||
testString: assert(store.getState().login === false);
|
||||
- text: 调度<code>loginAction</code>应该将store状态中的<code>login</code>属性更新为<code>true</code> 。
|
||||
- text: dispatch <code>loginAction</code>后应将 store 中 state 的<code>login</code>值更新为<code>true</code>。
|
||||
testString: assert((function() { const initialState = store.getState(); store.dispatch(loginAction()); const afterState = store.getState(); return initialState.login === false && afterState.login === true })());
|
||||
- text: 如果操作不是<code>LOGIN</code>类型,则存储应返回当前状态。
|
||||
- text: 如果 action 的类型不是<code>LOGIN</code>,那么 store 应返回当前的 state。
|
||||
testString: 'assert((function() { store.dispatch({type: ''__TEST__ACTION__''}); let afterTest = store.getState(); return typeof afterTest === ''object'' && afterTest.hasOwnProperty(''login'') })());'
|
||||
|
||||
```
|
||||
@ -42,9 +47,9 @@ const defaultState = {
|
||||
};
|
||||
|
||||
const reducer = (state = defaultState, action) => {
|
||||
// change code below this line
|
||||
// 修改此行下方的代码
|
||||
|
||||
// change code above this line
|
||||
// 修改此行上方的代码
|
||||
};
|
||||
|
||||
const store = Redux.createStore(reducer);
|
||||
@ -54,7 +59,6 @@ const loginAction = () => {
|
||||
type: 'LOGIN'
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -66,8 +70,31 @@ const loginAction = () => {
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
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'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,26 +3,32 @@ id: 5a24c314108439a4d4036158
|
||||
title: Never Mutate State
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 从不改变国家
|
||||
forumTopicId: 301445
|
||||
localeTitle: 永不改变状态
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">这些最后的挑战描述了在Redux中实施状态不变性关键原则的几种方法。不可变状态意味着您永远不会直接修改状态,而是返回状态的新副本。如果你随着时间的推移拍摄了一个Redux应用程序状态的快照,你会看到<code>state 1</code> , <code>state 2</code> , <code>state 3</code> , <code>state 4</code> , <code>...</code>等等,其中每个状态可能与最后一个状态相似,但每个状态是一个独特的数据。事实上,这种不变性提供了您可能听说过的时间旅行调试等功能。 Redux不会在其商店或减少者中主动强制执行状态不变性,而责任落在程序员身上。幸运的是,JavaScript(尤其是ES6)提供了一些有用的工具,可用于强制执行状态的不变性,无论是<code>string</code> , <code>number</code> , <code>array</code>还是<code>object</code> 。请注意,字符串和数字是原始值,并且本质上是不可变的。换句话说,3总是3.您不能更改数字3的值。但是, <code>array</code>或<code>object</code>是可变的。实际上,您的状态可能包含<code>array</code>或<code>object</code> ,因为它们是用于表示许多类型信息的有用数据结构。 </section>
|
||||
<section id='description'>
|
||||
这些最后的挑战描述了在 Redux 中强制执行状态不变性关键原则的几种方法。不可变状态意味着你永远不会直接修改状态,而是返回一个新的状态副本。
|
||||
如果你拍摄 Redux 应用程序状态的快照,你会看到类似<code>state 1</code>,<code>state 2</code>,<code>state 3</code>,<code>state 4</code>,<code>...</code>等等,每个状态可能与最后一个状态相似,但每个状态都是一个独特的数据。事实上,这种不变性是什么提供了你可能听说过的时间旅行调试等功能。
|
||||
Redux 并没有积极地在其 store 或者 reducer 中强制执行状态不变性,责任落在程序员身上。幸运的是,JavaScript(尤其是 ES6)提供了一些有用的工具,可以用来强制执行状态的不变性,无论是<code>string</code>,<code>number</code>,<code>array</code>或<code>object</code>。请注意,字符串和数字是原始值,并且本质上是不可变的。换句话说,3 总是 3,你不能改变数字 3 的值。然而,<code>array</code>或<code>object</code>是可变的。实际上,你的状态可能包括<code>array</code>或<code>object</code>,因为它们在表示许多类型信息的数据结构时非常有用。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">代码编辑器中有一个<code>store</code>和<code>reducer</code>器,用于管理待办事项。完成在<code>ADD_TO_DO</code>中写入<code>ADD_TO_DO</code>情况,以向状态附加新的待办事项。使用标准JavaScript或ES6可以通过几种方法实现此目的。看看是否可以找到一种方法来返回一个新数组,其中<code>action.todo</code>的项目附加到结尾。 </section>
|
||||
<section id='instructions'>
|
||||
代码编辑器中有一个<code>store</code>和<code>reducer</code>,用于管理待办事项。完成在 reducer 中编写<code>ADD_TO_DO</code>的情况,使用标准 JavaScript 或 ES6 可以通过几种方法来实现这一目标。看看是否可以找到一种方法来返回一个新数组,其中来自<code>action.todo</code>的项目添加到数组的末尾。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: Redux存储应该存在并使用等于代码编辑器中的<code>todos</code>数组的状态进行初始化。
|
||||
- text: Redux store 应该在代码编辑器中存在并使用名字为<code>todos</code>的数组进行状态初始化。
|
||||
testString: 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(','); })());
|
||||
- text: <code>addToDo</code>和<code>immutableReducer</code>都应该是函数。
|
||||
testString: assert(typeof addToDo === 'function' && typeof immutableReducer === 'function');
|
||||
- text: 在Redux存储上调度<code>ADD_TO_DO</code>类型的操作应该添加<code>todo</code> ,不应该改变状态。
|
||||
- text: 在 Redux store 上 dispatch 一个类型为<code>ADD_TO_DO</code>的aciton应该添加一个<code>todo</code>项,并且不应该改变 state。
|
||||
testString: 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)); })());
|
||||
|
||||
```
|
||||
@ -37,6 +43,48 @@ tests:
|
||||
```jsx
|
||||
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:
|
||||
// 不要在这里改变 state,否则测试将失败
|
||||
return
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
// 一个 todo 的例子是 'Learn React',
|
||||
const addToDo = (todo) => {
|
||||
return {
|
||||
type: ADD_TO_DO,
|
||||
todo
|
||||
}
|
||||
}
|
||||
|
||||
const store = Redux.createStore(immutableReducer);
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
const ADD_TO_DO = 'ADD_TO_DO';
|
||||
|
||||
// A list of strings representing tasks to do:
|
||||
const todos = [
|
||||
'Go to the store',
|
||||
@ -48,8 +96,7 @@ const todos = [
|
||||
const immutableReducer = (state = todos, action) => {
|
||||
switch(action.type) {
|
||||
case ADD_TO_DO:
|
||||
// don't mutate state here or the tests will fail
|
||||
return
|
||||
return state.concat(action.todo);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
@ -64,20 +111,6 @@ const addToDo = (todo) => {
|
||||
}
|
||||
|
||||
const store = Redux.createStore(immutableReducer);
|
||||
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
// solution required
|
||||
```
|
||||
|
||||
/section>
|
||||
|
@ -3,26 +3,30 @@ id: 5a24c314108439a4d4036153
|
||||
title: Register a Store Listener
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 注册商店监听器
|
||||
forumTopicId: 301446
|
||||
localeTitle: 注册 Store 监听器
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">您可以在Redux <code>store</code>对象上访问的另一种方法是<code>store.subscribe()</code> 。这允许您将监听器函数订阅到商店,只要针对商店调度操作,就会调用这些函数。此方法的一个简单用途是为您的商店订阅一个功能,只需在每次收到操作并更新商店时记录消息。 </section>
|
||||
<section id='description'>
|
||||
在 Redux <code>store</code>对象上访问数据的另一种方法是<code>store.subscribe()</code>。这允许你将监听器函数订阅到 store,只要一个 action 被 dispatch 就会调用它们。这个方法的一个简单用途是为你的 store 订阅一个函数,它只是在每次收到一个 action 并且更新 store 时记录一条消息。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">编写一个回调函数,每次商店收到一个动作时,它会递增全局变量<code>count</code> ,并将此函数传递给<code>store.subscribe()</code>方法。您将看到<code>store.dispatch()</code>连续三次被调用,每次都直接传入一个操作对象。观察操作调度之间的控制台输出以查看更新。 </section>
|
||||
<section id='instructions'>
|
||||
编写一个回调函数,每次 store 收到一个 action 时,它会递增全局变量<code>count</code>,并将此函数传递给<code>store.subscribe()</code>方法。你将会看到<code>store.dispatch()</code>连续三次被调用,每次都直接传入一个 action 对象。观察 dispatch action 之间的控制台输出,看看是否发生了更新。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 在商店上调度<code>ADD</code>操作应该将状态增加<code>1</code> 。
|
||||
- text: 在 store 上 dispatch <code>ADD</code> action 应该使计数器增加<code>1</code>。
|
||||
testString: 'assert((function() { const initialState = store.getState(); store.dispatch({ type: ''ADD'' }); const newState = store.getState(); return newState === (initialState + 1); })());'
|
||||
- text: 应该有一个使用<code>store.subscribe</code>订阅商店的监听器功能。
|
||||
- text: 应该有一个监听函数<code>store.subscribe</code>订阅 store。
|
||||
testString: getUserInput => assert(getUserInput('index').includes('store.subscribe('));
|
||||
- text: <code>store.subscribe</code>的回调还应该在存储更新时增加全局<code>count</code>变量。
|
||||
- text: 在更新 store 时,<code>store.subscribe</code>应该在回调中使全局变量<code>count</code>增加。
|
||||
testString: assert(store.getState() === count);
|
||||
|
||||
```
|
||||
@ -48,12 +52,12 @@ const reducer = (state = 0, action) => {
|
||||
|
||||
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);
|
||||
@ -61,7 +65,6 @@ store.dispatch({type: ADD});
|
||||
console.log(count);
|
||||
store.dispatch({type: ADD});
|
||||
console.log(count);
|
||||
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -71,7 +74,6 @@ console.log(count);
|
||||
|
||||
```jsx
|
||||
count = 0;
|
||||
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -82,8 +84,34 @@ count = 0;
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
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});
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,26 +3,30 @@ id: 5a24c314108439a4d403615a
|
||||
title: Remove an Item from an Array
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
forumTopicId: 301447
|
||||
localeTitle: 从数组中删除项目
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">是时候练习从数组中删除项目了。扩展运算符也可以在这里使用。其他有用的JavaScript方法包括<code>slice()</code>和<code>concat()</code> 。 </section>
|
||||
<section id='description'>
|
||||
是时候练习从数组中删除项目了。扩展运算符也可以在这里使用。其他有用的JavaScript方法包括<code>slice()</code>和<code>concat()</code>。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">修改了reducer和action creator,以根据项目的索引从数组中删除项目。完成编写reducer以便返回一个新的状态数组,并删除特定索引处的项。 </section>
|
||||
<section id='instructions'>
|
||||
reducer 和 action creator 被修改为根据项目的索引从数组中删除一个项目。完成编写 reducer 以便返回一个新的状态数组,并删除特定索引处的项目。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 'Redux存储应该存在并初始化为等于<code>[0,1,2,3,4,5]</code>的状态'
|
||||
- text: Redux store 应该存在并初始化一个<code>[0,1,2,3,4,5]</code>的状态。
|
||||
testString: assert((function() { const initialState = store.getState(); return (Array.isArray(initialState) === true && DeepEqual(initialState, [0, 1, 2, 3, 4, 5])); })());
|
||||
- text: <code>removeItem</code>和<code>immutableReducer</code>都应该是函数。
|
||||
- text: <code>removeItem</code>和<code>immutableReducer</code>都应该是一个函数。
|
||||
testString: assert(typeof removeItem === 'function' && typeof immutableReducer === 'function');
|
||||
- text: 调度<code>removeItem</code>动作创建者应该从状态中删除项目,不应该改变状态。
|
||||
- text: dispatch <code>removeItem</code> action creator 应该从 state 中删除项目,不应该改变 state。
|
||||
testString: 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]); })());
|
||||
|
||||
```
|
||||
@ -38,7 +42,7 @@ tests:
|
||||
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
|
||||
// 不要在这里改变 state 否则测试会失败。
|
||||
return
|
||||
default:
|
||||
return state;
|
||||
@ -53,7 +57,6 @@ const removeItem = (index) => {
|
||||
}
|
||||
|
||||
const store = Redux.createStore(immutableReducer);
|
||||
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -65,8 +68,28 @@ const store = Redux.createStore(immutableReducer);
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
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);
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,24 +3,30 @@ id: 5a24c314108439a4d4036155
|
||||
title: Send Action Data to the Store
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 将操作数据发送到商店
|
||||
forumTopicId: 301448
|
||||
localeTitle: 发送 Action Data 给 Store
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">到目前为止,您已经学会了如何将操作分派给Redux存储,但到目前为止,这些操作还没有包含除<code>type</code>之外的任何信息。您还可以发送特定数据以及您的操作。事实上,这是非常常见的,因为动作通常源于一些用户交互,并倾向于携带一些数据。 Redux商店经常需要了解这些数据。 </section>
|
||||
<section id='description'>
|
||||
到目前为止,你已经学会了如何将 action dispatch 给 Redux store,但到目前为止,这些 action 并未包含除 <code>type</code>之外的任何信息。你还可以发送特定数据和 action 一起。事实上,这是非常常见的,因为 action 通常源于一些用户交互,并且往往会携带一些数据,Redux store 经常需要知道这些数据。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">在代码编辑器中定义了一个基本的<code>notesReducer()</code>和一个<code>addNoteText()</code>动作创建器。完成<code>addNoteText()</code>函数的主体,以便它返回一个<code>action</code>对象。该对象应包含值为<code>ADD_NOTE</code>的<code>type</code>属性,以及设置为传递给action creator的<code>note</code>数据的<code>text</code>属性。当您致电操作创建者时,您将传递可以访问该对象的特定注释信息。接下来,完成在<code>notesReducer()</code>编写<code>switch</code>语句。您需要添加一个处理<code>addNoteText()</code>操作的案例。只要存在类型为<code>ADD_NOTE</code>的操作,就应该触发此情况,并且应该将传入<code>action</code>的<code>text</code>属性作为新<code>state</code> 。该操作将在代码底部发送。完成后,运行代码并观察控制台。这就是将特定于操作的数据发送到商店并在更新存储<code>state</code>时使用它所需的全部内容。 </section>
|
||||
<section id='instructions'>
|
||||
在代码编辑器中定义了一个基础的<code>notesReducer()</code>和<code>addNoteText()</code> action creator。完成<code>addNoteText()</code>函数的主体,这样它就会返回一个<code>action</code>对象。该对象应该包含一个<code>type</code>属性,其值为<code>ADD_NOTE</code>,还有一个<code>text</code>属性通过 action creator 将值设置为<code>note</code>。当你调用 action creator 时,你需要传入可以访问该对象的特定注释信息。
|
||||
接下来,完成在<code>notesReducer()</code>中编写的<code>switch</code>语句。你需要添加一个处理<code>addNoteText()</code>操作的选项。只要存在<code>ADD_NOTE</code>类型的 action,就应该触发 case,并且它应该在传入的<code>action</code>上返回<code>text</code>属性作为新的<code>state</code>
|
||||
这个 action 将在代码底部发送。一旦完成后,运行代码并观察控制台。这就是将特定于 action 的数据发送到 store 并在更新 store <code>state</code>时使用它所需的全部内容。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 动作创建者<code>addNoteText</code>应返回具有键<code>type</code>和<code>text</code>的对象。
|
||||
- text: action creator <code>addNoteText</code>应该返回一个包含<code>type</code>和<code>text</code>的对象。
|
||||
testString: assert((function() { const addNoteFn = addNoteText('__TEST__NOTE'); return addNoteFn.type === ADD_NOTE && addNoteFn.text === '__TEST__NOTE' })());
|
||||
- text: 使用<code>addNoteText</code>操作创建程序调度<code>ADD_NOTE</code>类型的操作应将<code>state</code>更新为传递给操作创建者的字符串。
|
||||
- text: dispatch 一个 action creator 是<code>addNoteText</code>的action<code>ADD_NOTE</code>,应将<code>state</code>更新为 action creator 传递的字符串。
|
||||
testString: assert((function() { const initialState = store.getState(); store.dispatch(addNoteText('__TEST__NOTE')); const newState = store.getState(); return initialState !== newState && newState === '__TEST__NOTE' })());
|
||||
|
||||
```
|
||||
@ -37,18 +43,18 @@ 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);
|
||||
@ -56,7 +62,6 @@ const store = Redux.createStore(notesReducer);
|
||||
console.log(store.getState());
|
||||
store.dispatch(addNoteText('Hello!'));
|
||||
console.log(store.getState());
|
||||
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -69,7 +74,33 @@ console.log(store.getState());
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
// solution required
|
||||
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());
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,32 +3,37 @@ id: 5a24c314108439a4d4036151
|
||||
title: Use a Switch Statement to Handle Multiple Actions
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 使用Switch语句处理多个操作
|
||||
forumTopicId: 301449
|
||||
localeTitle: 使用 Switch 语句处理多个 Actions
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">您可以告诉Redux商店如何处理多种操作类型。假设您在Redux商店中管理用户身份验证。您希望在用户登录和注销时具有状态表示。您使用经过<code>authenticated</code>的属性的单个状态对象来表示它。您还需要动作创建者创建与用户登录和用户注销相对应的操作,以及操作对象本身。 </section>
|
||||
<section id='description'>
|
||||
你可以定义 Redux store 如何处理多种 action 类型。比如你正在 Redux store 中进行用户身份验证,如果你希望用户在登录和注销时具有状态的响应,你可以使用具有<code>authenticated</code>属性的单个的 state 对象。你还需要使用 action creators 创建与用户登录和用户注销相对应的 action,以及 action 对象本身。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">代码编辑器为您设置了商店,操作和操作创建器。填写<code>reducer</code>函数以处理多个身份验证操作。在<code>reducer</code>使用JavaScript <code>switch</code>语句来响应不同的操作事件。这是编写Redux减速器的标准模式。 switch语句应该切换<code>action.type</code>并返回适当的身份验证状态。 <strong>注意:</strong>此时,不要担心状态不变性,因为在这个例子中它很小而且简单。对于每个操作,您可以返回一个新对象 - 例如, <code>{authenticated: true}</code> 。另外,不要忘记在switch语句中写一个返回当前<code>state</code>的<code>default</code>大小写。这很重要,因为一旦您的应用程序有多个Reducer,它们都会在执行操作调度时运行,即使操作与该reducer无关。在这种情况下,您需要确保返回当前<code>state</code> 。 </section>
|
||||
<section id='instructions'>
|
||||
代码编辑器为你创建了 store、actions、action creators。通过编写<code>reducer</code>函数来处理多个身份验证操作。可以在<code>reducer</code>通过使用 JavaScript 的<code>switch</code>来响应不同的 action 事件。这是编写 Redux reducer 时的标准模式,switch 语句选择<code>action.type</code>中的一个值并返回相应的身份验证状态。
|
||||
<strong>注意:</strong> 此时,不要担心 state 的不变性,因为在这个示例中它很小而且很简单。所以对于每个操作你都可以返回一个新对象,比如<code>{authenticated:true}</code>。另外,不要忘记在 switch 语句中写一个<code>default</code> case,返回当前的<code>state</code>。这是很重要的,因为一旦你的程序有多个 reducer,当一个 action 被 dispatch 时它们都会运行,即使 action 与该 reducer 无关。在这种情况下,你要确保返回当前的<code>state</code>
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 调用函数<code>loginUser</code>应该返回一个对象,其type属性设置为字符串<code>LOGIN</code> 。
|
||||
- text: '调用<code>loginUser</code>函数应该返回一个 {type:"LOGIN"} 对象。'
|
||||
testString: assert(loginUser().type === 'LOGIN');
|
||||
- text: 调用函数<code>logoutUser</code>应该返回一个对象,其type属性设置为字符串<code>LOGOUT</code> 。
|
||||
- text: '调用<code>logoutUser</code>函数应该返回一个 {type:"LOGOUT"} 对象。'
|
||||
testString: assert(logoutUser().type === 'LOGOUT');
|
||||
- text: 应使用经过<code>authenticated</code>属性设置为<code>false</code>的对象初始化存储。
|
||||
- text: 'store 应该设置一个初始化对象 {authenticated:false}。'
|
||||
testString: assert(store.getState().authenticated === false);
|
||||
- text: 调度<code>loginUser</code>应该将store状态中的<code>authenticated</code>属性更新为<code>true</code> 。
|
||||
- text: dispatch <code>loginUser</code>应该将 store 中的 state 的<code>authenticated</code>值更新为<code>true</code>。
|
||||
testString: assert((function() { const initialState = store.getState(); store.dispatch(loginUser()); const afterLogin = store.getState(); return initialState.authenticated === false && afterLogin.authenticated === true })());
|
||||
- text: 调度<code>logoutUser</code>应将store状态中的<code>authenticated</code>属性更新为<code>false</code> 。
|
||||
- text: dispatch <code>logoutUser</code>应该将 store 中的 state 的<code>authenticated</code>值更新为<code>false</code>。
|
||||
testString: assert((function() { store.dispatch(loginUser()); const loggedIn = store.getState(); store.dispatch(logoutUser()); const afterLogout = store.getState(); return loggedIn.authenticated === true && afterLogout.authenticated === false })());
|
||||
- text: <code>authReducer</code>函数应该使用<code>switch</code>语句处理多个动作类型。
|
||||
- text: <code>authReducer</code>函数应该使用<code>switch</code>语句处理多个 action 类型。
|
||||
testString: getUserInput => assert( getUserInput('index').toString().includes('switch') && getUserInput('index').toString().includes('case') && getUserInput('index').toString().includes('default'));
|
||||
|
||||
```
|
||||
@ -46,9 +51,9 @@ const defaultState = {
|
||||
};
|
||||
|
||||
const authReducer = (state = defaultState, action) => {
|
||||
// change code below this line
|
||||
// 修改此行下方的代码
|
||||
|
||||
// change code above this line
|
||||
// 修改此行上方的代码
|
||||
};
|
||||
|
||||
const store = Redux.createStore(authReducer);
|
||||
@ -64,7 +69,6 @@ const logoutUser = () => {
|
||||
type: 'LOGOUT'
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -76,8 +80,46 @@ const logoutUser = () => {
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
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'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,36 +3,41 @@ id: 5a24c314108439a4d4036152
|
||||
title: Use const for Action Types
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 将const用于Action Types
|
||||
forumTopicId: 301450
|
||||
localeTitle: 使用 const 声明 Action Types
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">使用Redux时的一种常见做法是将操作类型指定为只读常量,然后在使用它们的任何地方引用这些常量。您可以重构您正在使用的代码,将操作类型编写为<code>const</code>声明。 </section>
|
||||
<section id='description'>
|
||||
在使用 Redux 时的一个常见做法是将操作类型指定为只读,然后在任何使用它们的地方引用这些常量。你可以通过将 action types 使用<code>const</code>声明重构你正在使用的代码。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">将<code>LOGIN</code>和<code>LOGOUT</code>声明为<code>const</code>值,并将它们分别分配给字符串<code>'LOGIN'</code>和<code>'LOGOUT'</code> 。然后,编辑<code>authReducer()</code>和动作创建者以引用这些常量而不是字符串值。 <strong>注意:</strong>通常以全部大写形式写常量,这也是Redux中的标准做法。 </section>
|
||||
<section id='instructions'>
|
||||
将<code>LOGIN</code>和<code>LOGOUT</code>声明为<code>const</code>类型的值,并为它们分别分配字符串<code>'LOGIN'</code>和<code>'LOGOUT'</code>。然后,编辑<code>authReducer()</code>和 action creators 来引用这些常量而不是字符串值。
|
||||
<strong>注意:</strong> 通常以全部大写形式写出常量,这也是 Redux 的标准做法。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 调用函数<code>loginUser</code>应该返回一个对象,其<code>type</code>属性设置为字符串<code>LOGIN</code> 。
|
||||
- text: '调用<code>loginUser</code>函数应该返回一个 {type:"LOGIN"} 对象。'
|
||||
testString: assert(loginUser().type === 'LOGIN');
|
||||
- text: 调用函数<code>logoutUser</code>应该返回一个对象,其<code>type</code>属性设置为字符串<code>LOGOUT</code> 。
|
||||
- text: '调用<code>logoutUser</code>函数应该返回一个 {type:\"LOGOUT\"} 对象。'
|
||||
testString: assert(logoutUser().type === 'LOGOUT');
|
||||
- text: 应使用属性<code>login</code>设置为<code>false</code>的对象初始化存储。
|
||||
- text: store 应该初始化一个对象 {login:false}。
|
||||
testString: assert(store.getState().authenticated === false);
|
||||
- text: 调度<code>loginUser</code>应该将store状态中的<code>login</code>属性更新为<code>true</code> 。
|
||||
- text: dispatch <code>loginUser</code>以后应将 store 中的 state 的<code>login</code>值更新为<code>true</code>。
|
||||
testString: assert((function() { const initialState = store.getState(); store.dispatch(loginUser()); const afterLogin = store.getState(); return initialState.authenticated === false && afterLogin.authenticated === true })());
|
||||
- text: 调度<code>logoutUser</code>应将store状态中的<code>login</code>属性更新为<code>false</code> 。
|
||||
- text: dispatch <code>logoutUser</code>应将 store 中的 state 的<code>login</code>值更新为<code>false</code>。
|
||||
testString: assert((function() { store.dispatch(loginUser()); const loggedIn = store.getState(); store.dispatch(logoutUser()); const afterLogout = store.getState(); return loggedIn.authenticated === true && afterLogout.authenticated === false })());
|
||||
- text: <code>authReducer</code>函数应该使用switch语句处理多个动作类型。
|
||||
- text: <code>authReducer</code>函数应该使用 switch 语句处理多个 action 类型。
|
||||
testString: getUserInput => assert((function() { return typeof authReducer === 'function' && getUserInput('index').toString().includes('switch') && getUserInput('index').toString().includes('case') && getUserInput('index').toString().includes('default') })());
|
||||
- text: <code>LOGIN</code>和<code>LOGOUT</code>应声明为<code>const</code>值,并应分配<code>LOGIN</code>和<code>LOGOUT</code>字符串。
|
||||
testString: getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').toString().replace(/\s/g,''); return (noWhiteSpace.includes('constLOGIN=\'LOGIN\'') || noWhiteSpace.includes('constLOGIN="LOGIN"')) && (noWhiteSpace.includes('constLOGOUT=\'LOGOUT\'') || noWhiteSpace.includes('constLOGOUT="LOGOUT"')) })());
|
||||
- text: 动作创建者和减速器应该引用<code>LOGIN</code>和<code>LOGOUT</code>常量。
|
||||
- text: '应该使用<code>const LOGIN="LOGIN"</code>和<code>const LOGOUT="LOGOUT"</code>的方式声明<code>LOGIN</code>和<code>LOGOUT</code>。'
|
||||
testString: const noWhiteSpace = code.replace(/\s/g, ''); assert(/constLOGIN=(['"`])LOGIN\1/.test(noWhiteSpace) && /constLOGOUT=(['"`])LOGOUT\1/.test(noWhiteSpace));
|
||||
- text: action creator 和 reducer 中应该引用<code>LOGIN</code>和<code>LOGOUT</code>常量。
|
||||
testString: getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').toString().replace(/\s/g,''); return noWhiteSpace.includes('caseLOGIN:') && noWhiteSpace.includes('caseLOGOUT:') && noWhiteSpace.includes('type:LOGIN') && noWhiteSpace.includes('type:LOGOUT') })());
|
||||
|
||||
```
|
||||
@ -45,9 +50,9 @@ tests:
|
||||
<div id='jsx-seed'>
|
||||
|
||||
```jsx
|
||||
// change code below this line
|
||||
// 修改此行下方的代码
|
||||
|
||||
// change code above this line
|
||||
// 修改此行上方的代码
|
||||
|
||||
const defaultState = {
|
||||
authenticated: false
|
||||
@ -87,7 +92,6 @@ const logoutUser = () => {
|
||||
type: 'LOGOUT'
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -99,8 +103,49 @@ const logoutUser = () => {
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
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
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,30 +3,37 @@ id: 5a24c314108439a4d4036156
|
||||
title: Use Middleware to Handle Asynchronous Actions
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
forumTopicId: 301451
|
||||
localeTitle: 使用中间件处理异步操作
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">到目前为止,这些挑战已经避免讨论异步操作,但它们是Web开发中不可避免的一部分。在某些时候,您需要在Redux应用程序中调用异步端点,那么如何处理这些类型的请求? Redux提供专门为此目的而设计的中间件,称为Redux Thunk中间件。以下是如何在Redux中使用它的简要说明。要包含Redux Thunk中间件,请将其作为参数传递给<code>Redux.applyMiddleware()</code> 。然后,此语句作为<code>createStore()</code>函数的第二个可选参数提供。看一下编辑器底部的代码,看看这个。然后,要创建异步操作,您将在动作创建器中返回一个函数,该函数将<code>dispatch</code>作为参数。在此函数中,您可以分派操作并执行异步请求。在此示例中,使用<code>setTimeout()</code>调用模拟异步请求。在启动任何异步行为之前调度操作是很常见的,这样您的应用程序状态就知道正在请求某些数据(例如,此状态可能会显示加载图标)。然后,一旦收到数据,就会发送另一个操作,该操作将数据作为有效负载以及操作完成的信息。请记住,您将<code>dispatch</code>作为参数传递给此特殊操作创建者。这是您用于分派操作的方法,您只需将操作直接传递给调度,中间件就可以处理其余操作。 </section>
|
||||
<section id='description'>
|
||||
目前为止的挑战都在避免讨论异步操作,但它们是 Web 开发中不可避免的一部分。在某些时候,你需要在 Redux 应用程序中使用异步请求,那么如何处理这些类型的请求?Redux 中间件专为此目的而设计,称为 Redux Thunk 中间件。这里简要介绍如何在 Redux 中使用它。
|
||||
如果要使用 Redux Thunk 中间件,请将其作为参数传递给<code>Redux.applyMiddleware()</code>。然后将此函数作为第二个可选参数提供给<code>createStore()</code>函数,看一下编辑器底部的代码,然后,要创建一个异步的 action,你需要在 action creator 中返回一个以<code>dispatch</code>为参数的函数。在这个函数中,你可以 dispatch action 并执行异步请求。
|
||||
在此示例中,使用<code>setTimeout()</code>调用模拟异步请求。通常在执行异步行为之前 dispatch action,以便应用程序状态知道正在请求某些数据(例如,这个状态可以显示加载图标)。然后,一旦收到数据,就会发送另一个 action,这个 action 完成的时间将作为数据的一个有效值。
|
||||
请记住,你正在通过将<code>dispatch</code>作为参数传递给这个特殊的 action creator。你用于 dispatch 你的 action 时只需将 action 直接传递给 dispatch,中间件就可以处理其余的部分。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">在<code>handleAsync()</code>动作创建器中写入两个调度。调度<code>requestingData()</code>在之前<code>setTimeout()</code>模拟API调用)。然后,在收到(假装)数据后,调度<code>receivedData()</code>操作,传入此数据。现在您知道如何在Redux中处理异步操作。其他一切继续像以前一样。 </section>
|
||||
<section id='instructions'>
|
||||
在<code>handleAsync()</code>的 action creator 中编写两个 dispatch 事件。在<code>setTimeout()</code>(模拟 API 调用)之前 dispatch<code>requestingData()</code>。然后在收到(模拟)数据后,dispatch<code>receivedData()</code>action,传入这些数据。现在你知道如何处理 Redux 中的异步操作,其他所有操作都继续像以前一样。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: <code>requestingData</code>操作创建者应返回类型等于<code>REQUESTING_DATA</code>值的对象。
|
||||
- text: <code>requesData</code> action creator 应返回类型和值都为<code>REQUESTING_DATA</code>的对象。
|
||||
testString: assert(requestingData().type === REQUESTING_DATA);
|
||||
- text: <code>receivedData</code>操作创建者应返回类型等于<code>RECEIVED_DATA</code>值的对象。
|
||||
- text: <code>receivedData</code> action creator 应返回类型和值都等于<code>RECEIVED_DATA</code>的对象。
|
||||
testString: assert(receivedData('data').type === RECEIVED_DATA);
|
||||
- text: <code>asyncDataReducer</code>应该是一个函数。
|
||||
- text: <code>asyncDataReducer</code>必须是个函数。
|
||||
testString: assert(typeof asyncDataReducer === 'function');
|
||||
- text: 调度requestedData操作创建者应该将获取的store <code>state</code>属性更新为<code>true</code> 。
|
||||
- text: 分发 requestedData 后 action creator 应该将 store 中的<code>state</code>的 fetching 的值更新为 <code>true</code>。
|
||||
testString: assert((function() { const initialState = store.getState(); store.dispatch(requestingData()); const reqState = store.getState(); return initialState.fetching === false && reqState.fetching === true })());
|
||||
- text: 调度<code>handleAsync</code>应调度数据请求操作,然后在延迟后调度接收的数据操作。
|
||||
- text: 如果要 dispatch <code>handleAsync</code> 应先 dispatch 数据请求的 action,然后在收到请求结果后再 dispatch 接收的数据 action。
|
||||
testString: assert((function() { const noWhiteSpace = handleAsync.toString().replace(/\s/g,''); return noWhiteSpace.includes('dispatch(requestingData())') === true && noWhiteSpace.includes('dispatch(receivedData(data))') === true })());
|
||||
|
||||
```
|
||||
@ -47,13 +54,13 @@ const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users}
|
||||
|
||||
const handleAsync = () => {
|
||||
return function(dispatch) {
|
||||
// dispatch request action here
|
||||
// 在这里 dispatch 请求的 action
|
||||
|
||||
setTimeout(function() {
|
||||
let data = {
|
||||
users: ['Jeff', 'William', 'Alice']
|
||||
}
|
||||
// dispatch received data action here
|
||||
// 在这里 dispatch 接收到的数据 action
|
||||
|
||||
}, 2500);
|
||||
}
|
||||
@ -85,7 +92,6 @@ const store = Redux.createStore(
|
||||
asyncDataReducer,
|
||||
Redux.applyMiddleware(ReduxThunk.default)
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -97,8 +103,52 @@ const store = Redux.createStore(
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
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)
|
||||
);
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,28 +3,34 @@ id: 5a24c314108439a4d4036159
|
||||
title: Use the Spread Operator on Arrays
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 在阵列上使用Spread Operator
|
||||
forumTopicId: 301452
|
||||
localeTitle: 在数组中使用扩展运算符
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description"> ES6的一个解决方案是帮助在Redux中强制执行状态不变性,它是扩展运算符: <code>...</code>扩展运算符具有各种应用程序,其中一个非常适合于从现有阵列生成新阵列的先前挑战。这是相对较新的,但常用的语法。例如,如果你有一个数组<code>myArray</code>并且写: <code>let newArray = [...myArray];</code> <code>newArray</code>现在是<code>myArray</code>的克隆。两个阵列仍然分别存在于内存中。如果你执行像<code>newArray.push(5)</code>这样的变异, <code>myArray</code>不会改变。 <code>...</code>有效地<i>将</i> <code>myArray</code>的值<i>展开</i>到一个新数组中。要克隆数组但在新数组中添加其他值,可以编写<code>[...myArray, 'new value']</code> 。这将返回一个由<code>myArray</code>中的值和字符串<code>'new value'</code>组成的新数组作为最后一个值。扩展语法可以像这样在数组组合中多次使用,但重要的是要注意它只是生成数组的浅表副本。也就是说,它只为一维数组提供不可变的数组操作。 </section>
|
||||
<section id='description'>
|
||||
ES6 中有助于在 Redux 中强制执行状态不变性的一个解决方案是扩展运算符:<code>...</code>。扩展运算符具有很多的应用,其中一种非常适合通过一个已有的数组生成一个新数组。这是相对较新的,但常用的语法。例如,如果你有一个数组<code>myArray</code>并写:
|
||||
<code>let newArray = [...myArray];</code>
|
||||
<code>newArray</code>现在是<code>myArray</code>的克隆。两个数组仍然在内存中单独存在。如果你执行像<code>newArray.push(5)</code>这样的突变,<code>myArray</code>不会改变。<code>...</code>有效<i>将<code>myArray</code>中的值</i>传播到新数组中。要克隆数组但在新数组中添加其他值,可以编写<code>[... myArray,'new value']</code>。这将返回一个由<code>中的值组成的新数组。</code>myArray</code>和字符串<code>'new value'</code>作为最后一个值。扩展语法可以像这样在数组组合中多次使用,但重要的是要注意它只做一个浅拷贝这就是说,它只为一维数组提供了不可变的数组操作。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">添加待办事项时,使用spread运算符返回新的状态副本。 </section>
|
||||
<section id='instructions'>
|
||||
添加待办事项时,使用 spread 运算符返回新的状态副本。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 'Redux存储应该存在并初始化为等于<code>[Do not mutate state!]</code> 。'
|
||||
- text: Redux store 应该存在并初始化一个<code>[Do not mutate state!]</code>的状态。
|
||||
testString: assert((function() { const initialState = store.getState(); return ( Array.isArray(initialState) === true && initialState[0] === 'Do not mutate state!'); })());
|
||||
- text: <code>addToDo</code>和<code>immutableReducer</code>都应该是函数。
|
||||
- text: <code>addToDo</code>和<code>immutableReducer</code>都应该是一个函数。
|
||||
testString: assert(typeof addToDo === 'function' && typeof immutableReducer === 'function');
|
||||
- text: 在Redux存储上调度<code>ADD_TO_DO</code>类型的操作应该添加<code>todo</code> ,不应该改变状态。
|
||||
- text: 在 Redux store 上 dispatch 一个类型为<code>ADD_TO_DO</code> aciton 应该添加一个<code>todo</code>项,并且不应该改变 state。
|
||||
testString: 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)); })());
|
||||
- text: 应使用扩展运算符返回新状态。
|
||||
- text: 应使用扩展运算符返回新的 state。
|
||||
testString: getUserInput => assert(getUserInput('index').includes('...state'));
|
||||
|
||||
```
|
||||
@ -40,7 +46,7 @@ tests:
|
||||
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
|
||||
// 不要在这里改变 state 否则测试会失败。
|
||||
return
|
||||
default:
|
||||
return state;
|
||||
@ -55,7 +61,6 @@ const addToDo = (todo) => {
|
||||
}
|
||||
|
||||
const store = Redux.createStore(immutableReducer);
|
||||
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -67,8 +72,28 @@ const store = Redux.createStore(immutableReducer);
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
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);
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
@ -3,32 +3,36 @@ id: 5a24c314108439a4d4036157
|
||||
title: Write a Counter with Redux
|
||||
challengeType: 6
|
||||
isRequired: false
|
||||
videoUrl: ''
|
||||
localeTitle: 用Redux写一个计数器
|
||||
forumTopicId: 301453
|
||||
localeTitle: 用 Redux 写一个计数器
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">现在您已经了解了Redux的所有核心原则!您已经了解了如何创建操作和操作创建器,创建Redux存储,针对存储分派操作以及使用纯reducer设计状态更新。您甚至已经了解了如何使用reducer组合管理复杂状态并处理异步操作。这些示例过于简单,但这些概念是Redux的核心原则。如果您理解它们,那么您已准备好开始构建自己的Redux应用程序。接下来的挑战涵盖了有关<code>state</code>不变性的一些细节,但首先,这里是对迄今为止所学到的所有内容的回顾。 </section>
|
||||
<section id='description'>
|
||||
现在你已经了解了 Redux 的所有核心原则!你已经了解了如何创建 action 和 action creator,创建 Redux store,通过 store dispatch action,以及使用纯粹的 reducer 设计状态更新。你甚至已经看到过如何使用 reducer 组合管理复杂状态并处理异步操作。这些例子很简单,但这些概念是 Redux 的核心原则。如果你理解它们,你就可以开始构建自己的 Redux 应用了。接下来的挑战包括关于<code>state</code>不变性的一些细节,但是,这里是对你到目前为止学到的所有内容的回顾。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">在本课程中,您将从头开始使用Redux实现一个简单的计数器。代码编辑器中提供了基础知识,但您必须填写详细信息!使用提供的名称并定义<code>incAction</code>和<code>decAction</code>操作创建者, <code>decAction</code> <code>counterReducer()</code> , <code>INCREMENT</code>和<code>DECREMENT</code>操作类型,最后定义Redux <code>store</code> 。一旦完成,您应该能够发送<code>INCREMENT</code>或<code>DECREMENT</code>操作来增加或减少<code>store</code>保存的状态。祝你好运第一个Redux应用程序! </section>
|
||||
<section id='instructions'>
|
||||
在本课程中,你将从头开始使用 Redux 实现一个简单的计数器。基本知识在代码编辑器中提供,但你必须完成详细的内容!使用提供给你的名称并定义<code>incAction</code>和<code>decActio</code> action creator <code>counterReducer()</code>,<code>INCREMENT</code>和<code>DECREMENT</code> action 类型,最后是 Redux <code>store</code>。一旦完成,你应该能够 dispatch <code>INCREMENT</code>或<code>DECREMENT</code>动作来增加或减少<code>store</code>中保存的状态。开始构建你的第一个 Redux 应用程序吧,祝你好运!
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 动作创建者<code>incAction</code>应返回<code>type</code>等于<code>INCREMENT</code>值的动作对象
|
||||
- text: 'action creator <code>incAction</code>应返回一个 action 对象 {type:"INCREMENT"}。'
|
||||
testString: assert(incAction().type ===INCREMENT);
|
||||
- text: 动作创建者<code>decAction</code>应与返回动作对象<code>type</code>等于的值<code>DECREMENT</code>
|
||||
- text: 'action creator <code>decAction</code>应返回一个 action 对象 {type:"DECREMENT"}。'
|
||||
testString: assert(decAction().type === DECREMENT);
|
||||
- text: Redux存储应该以0 <code>state</code>初始化。
|
||||
- text: Redux store 应该将<code>state</code>初始化为 0。
|
||||
testString: assert(store.getState() === 0);
|
||||
- text: 在Redux存储上调度<code>incAction</code>应该将<code>state</code>增加1。
|
||||
- text: 在 Redux store 上 dispatch <code>incAction</code>应该将<code>state</code>增加 1。
|
||||
testString: assert((function() { const initialState = store.getState(); store.dispatch(incAction()); const incState = store.getState(); return initialState + 1 === incState })());
|
||||
- text: 在Redux存储上调度<code>decAction</code>应该将<code>state</code>减1。
|
||||
- text: 在 Redux store 上 dispatch <code>decAction</code>应该将<code>state</code>减少 1。
|
||||
testString: assert((function() { const initialState = store.getState(); store.dispatch(decAction()); const decState = store.getState(); return initialState - 1 === decState })());
|
||||
- text: <code>counterReducer</code>应该是一个函数
|
||||
- text: <code>counterReducer</code>必须是一个函数。
|
||||
testString: assert(typeof counterReducer === 'function');
|
||||
|
||||
```
|
||||
@ -41,17 +45,16 @@ tests:
|
||||
<div id='jsx-seed'>
|
||||
|
||||
```jsx
|
||||
const INCREMENT = null; // define a constant for increment action types
|
||||
const DECREMENT = null; // define a constant for decrement action types
|
||||
const INCREMENT = null; // 为增量 action 类型定义一个常量
|
||||
const DECREMENT = null; // 为减量 action 类型定义一个常量
|
||||
|
||||
const counterReducer = null; // define the counter reducer which will increment or decrement the state based on the action it receives
|
||||
const counterReducer = null; // 定义计数器,它将根据收到的action增加或减少状态
|
||||
|
||||
const incAction = null; // define an action creator for incrementing
|
||||
const incAction = null; // 定义一个用于递增的 action creator
|
||||
|
||||
const decAction = null; // define an action creator for decrementing
|
||||
|
||||
const store = null; // define the Redux store here, passing in your reducers
|
||||
const decAction = null; // 定义一个用于递减的 action creator
|
||||
|
||||
const store = null; // 在这里定义一个 Redux store,传递你的 reducer
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -63,8 +66,35 @@ const store = null; // define the Redux store here, passing in your reducers
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
|
||||
```js
|
||||
// solution required
|
||||
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);
|
||||
```
|
||||
|
||||
/section>
|
||||
</section>
|
||||
|
Reference in New Issue
Block a user