diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/combine-multiple-reducers.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/combine-multiple-reducers.chinese.md index 5fd5ee33f1..0805952417 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/combine-multiple-reducers.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/combine-multiple-reducers.chinese.md @@ -3,28 +3,43 @@ id: 5a24c314108439a4d4036154 title: Combine Multiple Reducers challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 结合多个减速器 +forumTopicId: 301436 +localeTitle: 组合多个 Reduces --- ## Description -
当您的应用程序的状态开始变得更加复杂时,可能很容易将状态分成多个部分。相反,请记住Redux的第一个原则:所有应用程序状态都保存在商店中的单个状态对象中。因此,Redux提供减速器组合作为复杂状态模型的解决方案。您可以定义多个reducers来处理应用程序状态的不同部分,然后将这些reducer组合成一个根reducer。然后将根reducer传递给Redux createStore()方法。为了让我们将多个reducer组合在一起,Redux提供了combineReducers()方法。此方法接受一个对象作为参数,您可以在其中定义将键与特定reducer函数关联的属性。 Redux将使用您为密钥指定的名称作为相关状态的名称。通常,当每个应用程序状态以某种方式不同或唯一时,为每个应用程序状态创建一个减速器是一个好习惯。例如,在具有用户身份验证的笔记记录应用程序中,一个reducer可以处理身份验证,而另一个reducer处理用户正在提交的文本和备注。对于这样的应用程序,我们可能会像这样编写combineReducers()方法:
const rootReducer = Redux.combineReducers({
auth:authenticationReducer,
笔记:notesReducer
});
现在,关键notes将包含与我们的注释相关联的所有状态,并由notesReducer处理。这就是如何组合多个reducers来管理更复杂的应用程序状态。在此示例中,Redux存储中保存的状态将是包含authnotes属性的单个对象。
+
+当你应用程序的状态开始变得越来越复杂时,将状态划分为多个部分可能是个更好的选择。相反,请记住 Redux 的第一个原则:所有应用程序状态都保存在 store 中的一个简单的 state 对象中。因此,Redux 提供 reducer 组合作为复杂状态模型的解决方案。定义多个 reducer 来处理应用程序状态的不同部分,然后将这些 reducer 组合成一个 root reducer。然后将 root reducer 传递给 Redux createStore()方法。 +为了让我们将可以将多个 reducer 组合在一起,Redux 提供了combineReducers()方法。该方法接受一个对象作为参数,在该参数中定义一个将键与特定 reducer 函数关联的属性。Redux 将使用你给的键值作为关联状态的名称。 +通常情况下,当它们在某种程度上是独一无二的,为每个应用程序的 state 创建一个减少器是一个很好的做法。例如,在一个带有用户身份验证的记笔记应用程序中,一个 reducer 可以处理身份验证而另一个处理用户提交的文本和注释。对于这样的应用程序,我们可能会编写combineReducers()方法,如下所示: + +```js +const rootReducer = Redux.combineReducers({ + auth: authenticationReducer, + notes: notesReducer +}); +``` + +现在,notes键将包含与我们的注释相关联的所有状态,并由我们的notesReducer处理。这就是如何组合多个 reducer 来管理更复杂的应用程序状态,在此示例中,Redux store 中保存的状态将是一个包含authnotes属性的简单对象。 +
## Instructions -
代码编辑器中提供了authReducer() counterReducer()authReducer()函数以及Redux存储。使用Redux.combineReducers()方法完成rootReducer()函数的Redux.combineReducers() 。指定counterReducer一键叫countauthReducer一个叫关键auth
+
+代码编辑器中提供了counterReducer()authReducer()函数以及 Redux store。使用Redux.combineReducers()方法编写完成rootReducer()函数。将counterReducer分配给一个叫做count的键,将authReducer分配给一个叫做auth的键。 +
## Tests
```yml tests: - - text: counterReducer应该递增和递减state 。 + - text: counterReducer应该递增和递减state。 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: authReducer应切换在truefalse之间进行authenticatedstate 。 + - text: authenticatedstate值应该在truefalse之间切换。 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: 存储state应该有两个键: count ,它包含一个数字, auth ,它包含一个对象。 auth对象应具有authenticated属性,该属性包含布尔值。 + - text: store state应该有两个 key:一个是count,它包含一个数字。另一个auth,它包含一个对象。auth对象应该具有authenticated的属性,该属性的值应该为布尔值。 testString: "assert((function() { const state = store.getState(); return typeof state.auth === 'object' && typeof state.auth.authenticated === 'boolean' && typeof state.count === 'number' })());" - - text: 该rootReducer应该是结合了功能counterReducerauthReducer 。 + - text: rootReducer应该是一个合并了counterReducerauthReducer的函数。 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
+ ```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> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/copy-an-object-with-object.assign.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/copy-an-object-with-object.assign.chinese.md index e28e5b1fc4..def9a470ba 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/copy-an-object-with-object.assign.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/copy-an-object-with-object.assign.chinese.md @@ -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 -
最后几个挑战适用于数组,但是当状态是一个object时,有一些方法可以帮助强制执行状态不变性。处理对象的有用工具是Object.assign()实用程序。 Object.assign()接受目标对象和源对象,并将源对象中的属性映射到目标对象。任何匹配的属性都会被源对象中的属性覆盖。此行为通常用于通过将空对象作为第一个参数传递,然后传递要复制的对象来生成对象的浅副本。这是一个例子: const newObject = Object.assign({}, obj1, obj2);这将newObject创建为一个新object ,其中包含当前存在于obj1obj2中的属性。
+
+最后几个挑战适用于数组,但是当状态是object时,有一些方法可以帮助强制执行状态不变性。处理对象的一个方法是Object.assign() Object.assign()获取目标对象和源对象,并将源对象中的属性映射到目标对象。任何匹配的属性都会被源对象中的属性覆盖。通常用于通过传递一个空对象作为第一个参数,然后是要用复制的对象来制作对象的浅表副本。这是一个例子: +const newObject = Object.assign({}, obj1, obj2); +这会创建newObject作为新的object,其中包含obj1obj2中当前存在的属性。 +
## Instructions -
Redux状态和操作被修改为处理stateobject 。编辑代码以返回类型为ONLINE操作的新state对象,该status属性将status属性设置为online字符串。尝试使用Object.assign()来完成挑战。
+
+Redux 状态和 action 被修改为处理state对象。编辑代码以返回一个新的state对象,用于类型的 actionONLINE,它将status属性设置为字符串online。尝试使用Object.assign()来完成挑战。 +
## Tests
```yml tests: - - text: Redux存储应该存在并使用等效于第1行声明的defaultState对象的状态进行初始化。 + - text: Redux store 应该存在并使用与第 1 行声明的defaultState对象相同的状态进行初始化。 testString: 'assert((function() { const expectedState = { user: ''CamperBot'', status: ''offline'', friends: ''732,982'', community: ''freeCodeCamp'' }; const initialState = store.getState(); return DeepEqual(expectedState, initialState); })());' - text: wakeUpimmutableReducer都应该是函数。 testString: assert(typeof wakeUp === 'function' && typeof immutableReducer === 'function'); - - text: 调度ONLINE类型的操作应该将status中的属性status更新为online并且不应该改变状态。 + - text: dispatch 一个类型为ONLINE的 action 应该将状态status更新为online,并且不应该改变状态。 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: Object.assign应该用于返回新状态。 + - text: Object.assign应该被用于返回一个新状态。 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); - ``` @@ -73,8 +78,33 @@ const store = Redux.createStore(immutableReducer); ## 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> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/create-a-redux-store.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/create-a-redux-store.chinese.md index 82a094a58a..ff6d7c3a4e 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/create-a-redux-store.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/create-a-redux-store.chinese.md @@ -3,24 +3,32 @@ id: 5a24c314108439a4d403614b title: Create a Redux Store challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 创建一个Redux商店 +forumTopicId: 301439 +localeTitle: 创建一个 Redux Store --- ## Description -
Redux是一个状态管理框架,可以与许多不同的Web技术一起使用,包括React。在Redux中,有一个状态对象负责应用程序的整个状态。这意味着如果您有一个包含十个组件的React应用程序,并且每个组件都有自己的本地状态,则应用程序的整个状态将由Redux store的单个状态对象定义。这是学习Redux时理解的第一个重要原则:Redux商店是应用程序状态的唯一真实来源。这也意味着,只要您的应用程序的任何部分想要更新状态,它必须通过Redux商店执行此操作。单向数据流可以更轻松地跟踪应用程序中的状态管理。
+
+Redux 是一个状态管理框架,可以与包括 React 在内的许多不同的 Web 技术一起使用。 +在 Redux 中,有一个状态对象负责应用程序的整个状态,这意味着如果你有一个包含十个组件且每个组件都有自己的本地状态的 React 项目,那么这个项目的整个状态将通过 Reduxstore被定义为单个状态对象,这是学习 Redux 时要理解的第一个重要原则:Redux store 是应用程序状态的唯一真实来源。 +这也意味着,如果你的应用程序想要更新状态,只能通过 Redux store 执行,单向数据流可以更轻松地对应用程序中的状态进行监测管理。 +
## Instructions -
Redux store是一个保存和管理应用程序state的对象。 Redux对象上有一个名为createStore()的方法,您可以使用该方法创建Redux store 。此方法将reducer函数作为必需参数。 reducer函数将在稍后的挑战中介绍,并且已在代码编辑器中为您定义。它只是将state作为参数并返回state 。声明一个store变量并将其赋值给createStore()方法,并将reducer作为参数传入。 注意:编辑器中的代码使用ES6默认参数语法初始化此状态以保存值5 。如果您不熟悉默认参数,可以参考课程中涵盖此主题的ES6部分
+
+Redux store是一个保存和管理应用程序状态的state,你可以使用 Redux 对象中的createStore()来创建一个 reduxstore,此方法将reducer函数作为必需参数,reducer函数将在后面的挑战中介绍。该函数已在代码编辑器中为你定义,它只需将state作为参数并返回一个state即可。 +声明一个store变量并把它分配给createStore()方法,然后把reducer作为一个参数传入即可。 +注意: 编辑器中的代码使用 ES6 默认参数语法将 state 的值初始化为5, 如果你不熟悉默认参数,你可以参考 ES6 全部课程,它里面涵盖了这个内容。 +
## 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
+ ```js -// solution required +const reducer = (state = 5) => { + return state; +} + +//Redux 方法可以从 Redux 对象获得 +// 例如: Redux.createStore() +// 在这里定义一个 store: + +const store = Redux.createStore(reducer); ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/define-a-redux-action.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/define-a-redux-action.chinese.md index ddf880d24b..4ec6ab7bf2 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/define-a-redux-action.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/define-a-redux-action.chinese.md @@ -3,24 +3,29 @@ id: 5a24c314108439a4d403614d title: Define a Redux Action challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 定义Redux动作 +forumTopicId: 301440 +localeTitle: 定义一个 Redux Action --- ## Description -
由于Redux是一个状态管理框架,因此更新状态是其核心任务之一。在Redux中,所有状态更新都由调度操作触发。操作只是一个JavaScript对象,其中包含有关已发生的操作事件的信息。 Redux存储接收这些操作对象,然后相应地更新其状态。有时,Redux操作也会携带一些数据。例如,操作在用户登录后携带用户名。虽然数据是可选的,但操作必须带有type属性,该属性指定发生的操作的“类型”。将Redux操作视为信使,将有关应用程序中发生的事件的信息提供给Redux商店。然后,商店根据发生的操作开展更新状态的业务。
+
+由于 Redux 是一个状态管理框架,因此更新状态是其核心任务之一。在 Redux 中,所有状态更新都由 dispatch action 触发,action 只是一个 JavaScript 对象,其中包含有关已发生的 action 事件的信息。Redux store 接收这些 action 对象,然后更新相应的状态。有时,Redux action 也会携带一些数据。例如,在用户登录后携带用户名,虽然数据是可选的,但 action 必须带有type属性,该属性表示此 action 的类型。 +我们可以将 Redux action 视为信使,将有关应用程序中发生的事件信息提供给 Redux store,然后 store 根据发生的 action 进行状态的更新。 +
## Instructions -
编写Redux操作就像声明具有type属性的对象一样简单。声明一个对象action并为其设置一个属性type设置为字符串'LOGIN'
+
+编写 Redux action 就像声明具有 type 属性的对象一样简单,声明一个对象action并为它设置一个属性type,并将他的值设置成字符串'LOGIN'。 +
## Tests
```yml tests: - - text: 应该存在一个操作对象。 + - text: 应该有一个 action 对象。 testString: assert((function() { return typeof action === 'object' })()); - - text: 该操作应具有值为LOGIN的键属性类型。 + - text: 该 action 的值应该为LOGIN。 testString: assert((function() { return action.type === 'LOGIN' })()); ``` @@ -33,7 +38,7 @@ tests:
```jsx -// Define an action here: +// 在此处定义 action ``` @@ -46,8 +51,11 @@ tests: ## Solution
+ ```js -// solution required +const action = { + type: 'LOGIN' +} ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/define-an-action-creator.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/define-an-action-creator.chinese.md index ce749d1c2e..b7339dec6a 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/define-an-action-creator.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/define-an-action-creator.chinese.md @@ -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 -
创建操作后,下一步是将操作发送到Redux存储,以便它可以更新其状态。在Redux中,您可以定义动作创建器来完成此任务。动作创建者只是一个返回动作的JavaScript函数。换句话说,动作创建者创建表示动作事件的对象。
+
+创建 action 后要将 action 发送到 Redux store,以便它可以更新其状态。在 Redux 中,你可以定义动作创建器来完成此任务,action creator 只是一个返回动作的 JavaScript 函数,换句话说,action creator 创建表示动作事件的对象。 +
## Instructions -
定义一个名为actionCreator()的函数,该函数在调用时返回action对象。
+
+定义名为actionCreator()的函数,该函数在调用时返回action对象。 +
## Tests
@@ -20,9 +24,9 @@ localeTitle: 定义一个Action Creator tests: - text: 函数actionCreator应该存在。 testString: assert(typeof actionCreator === 'function'); - - text: 运行actionCreator函数应该返回操作对象。 + - text: 运行actionCreator函数应返回 action 对象。 testString: assert(typeof action === 'object'); - - text: 返回的操作应具有值为LOGIN的键属性类型。 + - text: 返回的 action 应具有值为LOGIN的键值类型。 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
+ ```js -// solution required +const action = { + type: 'LOGIN' +} +// 在此处定义 action creator: +const actionCreator = () => { + return action; +}; ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/dispatch-an-action-event.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/dispatch-an-action-event.chinese.md index 75c4266bb9..b17afb749e 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/dispatch-an-action-event.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/dispatch-an-action-event.chinese.md @@ -3,26 +3,37 @@ id: 5a24c314108439a4d403614f title: Dispatch an Action Event challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 派遣行动事件 +forumTopicId: 301442 +localeTitle: 分发 Action Event --- ## Description -
dispatch方法是您用于将操作分派给Redux存储的方法。调用store.dispatch()并传递从操作创建者返回的值会将操作发送回商店。回想一下,动作创建者返回一个具有type属性的对象,该属性指定已发生的动作。然后,该方法将操作对象调度到Redux存储。根据之前的挑战示例,以下行是等效的,并且都调度LOGIN类型的操作:
store.dispatch(actionCreator());
store.dispatch({type:'LOGIN'});
+
+dispatch方法用于将 action 分派给 Redux store,调用store.dispatch()将从 action creator 返回的值发送回 store。 +action creator 返回一个具有 type 属性的对象,该属性指定已发生的 action,然后,该方法将 action 对象 dispatch 到 Redux store,根据之前的挑战示例,以下内容是等效的,并且都 dispatch 类型为LOGIN的 action: + +```js +store.dispatch(actionCreator()); +store.dispatch({ type: 'LOGIN' }); +``` + +
## Instructions -
代码编辑器中的Redux存储具有初始化状态,该状态是包含当前设置为falselogin属性的对象。还有一个名为loginAction()的动作创建器,它返回LOGIN类型的动作。通过调用dispatch方法将LOGIN操作发送到Redux存储,并传入loginAction()创建的操作。
+
+代码编辑器中的 Redux store 具有初始化状态对象{login:'false'},还有一个名为loginAction()的 action creator,它返回类型为LOGIN的 action,然后通过调用dispatch方法将LOGIN的 action dispatch 给 Redux store,并传递loginAction()创建的 action。 +
## Tests
```yml tests: - - text: 调用函数loginAction应该返回一个对象,其type属性设置为字符串LOGIN 。 + - text: '调用函数loginAction应该返回一个对象{type:"LOGIN"}。' testString: assert(loginAction().type === 'LOGIN'); - - text: 应使用属性login设置为false的对象初始化存储。 + - text: 'store 应该初始化一个对象 {login:false}。' testString: assert(store.getState().login === false); - - text: store.dispatch()方法应该用于调度LOGIN类型的操作。 + - text: store.dispatch()方法应该被用于 dispatch 一个类型为LOGIN的 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
+ ```js -// solution required +const store = Redux.createStore( + (state = {login: false}) => state +); + +const loginAction = () => { + return { + type: 'LOGIN' + } +}; + +// Dispatch the action here: +store.dispatch(loginAction()); ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/get-state-from-the-redux-store.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/get-state-from-the-redux-store.chinese.md index 44b6a0257e..1027e0b03e 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/get-state-from-the-redux-store.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/get-state-from-the-redux-store.chinese.md @@ -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 -
Redux存储对象提供了几种允许您与之交互的方法。例如,您可以使用getState()方法检索Redux存储对象中保存的当前state
+
+Redux store 对象提供了几种允许你与之交互的方法,你可以使用getState()方法检索 Redux store 对象中保存的当前的state。 +
## Instructions -
上一个挑战中的代码在代码编辑器中更简洁地重写。使用store.getState()store检索state ,并将其分配给新变量currentState
+
+在代码编辑器中可以将上一个挑战中的代码更简洁地重写,在store中使用store.getState()检索state,并将其分配给新变量currentState。 +
## Tests
```yml tests: - - text: 对于初始状态,redux存储的值应为5。 + - text: redux store 的 state 应该有一个初始值 5。 testString: assert(store.getState()===5); - - text: 应该存在一个变量currentState ,并且应该为其分配Redux存储的当前状态。 + - text: 应该存在一个变量currentState,并为其分配 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
+ ```js -// solution required +const store = Redux.createStore( + (state = 5) => state +); + +// 更改此行下方的代码 +const currentState = store.getState(); ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/handle-an-action-in-the-store.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/handle-an-action-in-the-store.chinese.md index e7cb70d0c0..97fb684519 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/handle-an-action-in-the-store.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/handle-an-action-in-the-store.chinese.md @@ -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 -
创建并分派操作后,Redux存储需要知道如何响应该操作。这是reducer功能的工作。 Redux中的Reducers负责响应操作而进行的状态修改。 reducerstateaction作为参数,并且它总是返回一个新state 。重要的是要看到这是减速器的唯一作用。它没有任何副作用 - 它从不调用API端点,它从来没有任何隐藏的意外。 reducer只是一个纯函数,它接受状态和动作,然后返回新状态。 Redux的另一个关键原则是state是只读的。换句话说, reducer函数必须始终返回一个新的state副本,而不是直接修改状态。 Redux不强制执行状态不变性,但是,您负责在reducer函数的代码中强制执行它。你将在以后的挑战中练习这一点。
+
+在一个 action 被创建并 dispatch 之后,Redux store 需要知道如何响应该操作。这就是reducer函数存在的意义。Redux 中的 Reducers 负责响应 action 然后进行状态的修改。reducerstateaction作为参数,并且它总是返回一个新的state。我们要知道这是 reducer 的唯一的作用。它不应有任何其他的作用:比如它不应调用 API 接口,也不应存在任何潜在的副作用。reducer 只是一个接受状态和动作,然后返回新状态的纯函数。 +Redux 的另一个关键原则是state是只读的。换句话说,reducer函数必须始终返回一个新的state,并且永远不会直接修改状态。Redux 不强制改变状态,但是你需要在你的 reducer 函数的代码中强制执行它,你将在以后的挑战中练习这一点。 +
## Instructions -
代码编辑器具有前面的示例以及为您启动reducer功能。填写reducer函数的主体,这样如果它收到'LOGIN'类型的动作,它将返回一个login设置为true的状态对象。否则,它返回当前state 。请注意,当前state和调度的action将传递给reducer,因此您可以使用action.type直接访问操作的类型。
+
+代码编辑器中具有前面的示例以及一个reducer函数。你需要完善reducer函数的内容,使得它如果收到类型为'LOGIN'的action,它将返回一个将login设置为true的 state 对象。否则,它就返回当前的state。请注意,当前state和dispatch的action将被传递给reducer,因此你可以使用action.type直接获取 action 的类型。 +
## Tests
```yml tests: - - text: 调用函数loginAction应该返回一个对象,其type属性设置为字符串LOGIN 。 + - text: '调用loginAction函数应该返回一个对象 {type:"LOGIN"}。' testString: assert(loginAction().type === 'LOGIN'); - - text: 应使用属性login设置为false的对象初始化存储。 + - text: 'store 应该初始化一个对象 {login:false}。' testString: assert(store.getState().login === false); - - text: 调度loginAction应该将store状态中的login属性更新为true 。 + - text: dispatch loginAction后应将 store 中 state 的login值更新为true。 testString: assert((function() { const initialState = store.getState(); store.dispatch(loginAction()); const afterState = store.getState(); return initialState.login === false && afterState.login === true })()); - - text: 如果操作不是LOGIN类型,则存储应返回当前状态。 + - text: 如果 action 的类型不是LOGIN,那么 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' } }; - ```
@@ -66,8 +70,31 @@ const loginAction = () => { ## 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> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/never-mutate-state.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/never-mutate-state.chinese.md index 2ad4d2823f..dc5dab3e99 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/never-mutate-state.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/never-mutate-state.chinese.md @@ -3,26 +3,32 @@ id: 5a24c314108439a4d4036158 title: Never Mutate State challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 从不改变国家 +forumTopicId: 301445 +localeTitle: 永不改变状态 --- ## Description -
这些最后的挑战描述了在Redux中实施状态不变性关键原则的几种方法。不可变状态意味着您永远不会直接修改状态,而是返回状态的新副本。如果你随着时间的推移拍摄了一个Redux应用程序状态的快照,你会看到state 1state 2state 3state 4...等等,其中每个状态可能与最后一个状态相似,但每个状态是一个独特的数据。事实上,这种不变性提供了您可能听说过的时间旅行调试等功能。 Redux不会在其商店或减少者中主动强制执行状态不变性,而责任落在程序员身上。幸运的是,JavaScript(尤其是ES6)提供了一些有用的工具,可用于强制执行状态的不变性,无论是stringnumberarray还是object 。请注意,字符串和数字是原始值,并且本质上是不可变的。换句话说,3总是3.您不能更改数字3的值。但是, arrayobject是可变的。实际上,您的状态可能包含arrayobject ,因为它们是用于表示许多类型信息的有用数据结构。
+
+这些最后的挑战描述了在 Redux 中强制执行状态不变性关键原则的几种方法。不可变状态意味着你永远不会直接修改状态,而是返回一个新的状态副本。 +如果你拍摄 Redux 应用程序状态的快照,你会看到类似state 1state 2state 3state 4...等等,每个状态可能与最后一个状态相似,但每个状态都是一个独特的数据。事实上,这种不变性是什么提供了你可能听说过的时间旅行调试等功能。 +Redux 并没有积极地在其 store 或者 reducer 中强制执行状态不变性,责任落在程序员身上。幸运的是,JavaScript(尤其是 ES6)提供了一些有用的工具,可以用来强制执行状态的不变性,无论是stringnumberarrayobject。请注意,字符串和数字是原始值,并且本质上是不可变的。换句话说,3 总是 3,你不能改变数字 3 的值。然而,arrayobject是可变的。实际上,你的状态可能包括arrayobject,因为它们在表示许多类型信息的数据结构时非常有用。 +
## Instructions -
代码编辑器中有一个storereducer器,用于管理待办事项。完成在ADD_TO_DO中写入ADD_TO_DO情况,以向状态附加新的待办事项。使用标准JavaScript或ES6可以通过几种方法实现此目的。看看是否可以找到一种方法来返回一个新数组,其中action.todo的项目附加到结尾。
+
+代码编辑器中有一个storereducer,用于管理待办事项。完成在 reducer 中编写ADD_TO_DO的情况,使用标准 JavaScript 或 ES6 可以通过几种方法来实现这一目标。看看是否可以找到一种方法来返回一个新数组,其中来自action.todo的项目添加到数组的末尾。 +
## Tests
```yml tests: - - text: Redux存储应该存在并使用等于代码编辑器中的todos数组的状态进行初始化。 + - text: Redux store 应该在代码编辑器中存在并使用名字为todos的数组进行状态初始化。 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: addToDoimmutableReducer都应该是函数。 testString: assert(typeof addToDo === 'function' && typeof immutableReducer === 'function'); - - text: 在Redux存储上调度ADD_TO_DO类型的操作应该添加todo ,不应该改变状态。 + - text: 在 Redux store 上 dispatch 一个类型为ADD_TO_DO的aciton应该添加一个todo项,并且不应该改变 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); +``` + + + + + +
+ +## 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); - ``` - - - -
- -## Solution -
- -```js -// solution required -``` - -/section> diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/register-a-store-listener.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/register-a-store-listener.chinese.md index 55262d9f21..2cdbfe8bc2 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/register-a-store-listener.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/register-a-store-listener.chinese.md @@ -3,26 +3,30 @@ id: 5a24c314108439a4d4036153 title: Register a Store Listener challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 注册商店监听器 +forumTopicId: 301446 +localeTitle: 注册 Store 监听器 --- ## Description -
您可以在Redux store对象上访问的另一种方法是store.subscribe() 。这允许您将监听器函数订阅到商店,只要针对商店调度操作,就会调用这些函数。此方法的一个简单用途是为您的商店订阅一个功能,只需在每次收到操作并更新商店时记录消息。
+
+在 Redux store对象上访问数据的另一种方法是store.subscribe()。这允许你将监听器函数订阅到 store,只要一个 action 被 dispatch 就会调用它们。这个方法的一个简单用途是为你的 store 订阅一个函数,它只是在每次收到一个 action 并且更新 store 时记录一条消息。 +
## Instructions -
编写一个回调函数,每次商店收到一个动作时,它会递增全局变量count ,并将此函数传递给store.subscribe()方法。您将看到store.dispatch()连续三次被调用,每次都直接传入一个操作对象。观察操作调度之间的控制台输出以查看更新。
+
+编写一个回调函数,每次 store 收到一个 action 时,它会递增全局变量count,并将此函数传递给store.subscribe()方法。你将会看到store.dispatch()连续三次被调用,每次都直接传入一个 action 对象。观察 dispatch action 之间的控制台输出,看看是否发生了更新。 +
## Tests
```yml tests: - - text: 在商店上调度ADD操作应该将状态增加1 。 + - text: 在 store 上 dispatch ADD action 应该使计数器增加1。 testString: 'assert((function() { const initialState = store.getState(); store.dispatch({ type: ''ADD'' }); const newState = store.getState(); return newState === (initialState + 1); })());' - - text: 应该有一个使用store.subscribe订阅商店的监听器功能。 + - text: 应该有一个监听函数store.subscribe订阅 store。 testString: getUserInput => assert(getUserInput('index').includes('store.subscribe(')); - - text: store.subscribe的回调还应该在存储更新时增加全局count变量。 + - text: 在更新 store 时,store.subscribe应该在回调中使全局变量count增加。 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); - ``` @@ -71,7 +74,6 @@ console.log(count); ```jsx count = 0; - ``` @@ -82,8 +84,34 @@ count = 0; ## 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> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/remove-an-item-from-an-array.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/remove-an-item-from-an-array.chinese.md index 7022486665..545e800b52 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/remove-an-item-from-an-array.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/remove-an-item-from-an-array.chinese.md @@ -3,26 +3,30 @@ id: 5a24c314108439a4d403615a title: Remove an Item from an Array challengeType: 6 isRequired: false -videoUrl: '' +forumTopicId: 301447 localeTitle: 从数组中删除项目 --- ## Description -
是时候练习从数组中删除项目了。扩展运算符也可以在这里使用。其他有用的JavaScript方法包括slice()concat()
+
+是时候练习从数组中删除项目了。扩展运算符也可以在这里使用。其他有用的JavaScript方法包括slice()concat()。 +
## Instructions -
修改了reducer和action creator,以根据项目的索引从数组中删除项目。完成编写reducer以便返回一个新的状态数组,并删除特定索引处的项。
+
+reducer 和 action creator 被修改为根据项目的索引从数组中删除一个项目。完成编写 reducer 以便返回一个新的状态数组,并删除特定索引处的项目。 +
## Tests
```yml tests: - - text: 'Redux存储应该存在并初始化为等于[0,1,2,3,4,5]的状态' + - text: Redux store 应该存在并初始化一个[0,1,2,3,4,5]的状态。 testString: assert((function() { const initialState = store.getState(); return (Array.isArray(initialState) === true && DeepEqual(initialState, [0, 1, 2, 3, 4, 5])); })()); - - text: removeItemimmutableReducer都应该是函数。 + - text: removeItemimmutableReducer都应该是一个函数。 testString: assert(typeof removeItem === 'function' && typeof immutableReducer === 'function'); - - text: 调度removeItem动作创建者应该从状态中删除项目,不应该改变状态。 + - text: dispatch removeItem 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); - ``` @@ -65,8 +68,28 @@ const store = Redux.createStore(immutableReducer); ## 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> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/send-action-data-to-the-store.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/send-action-data-to-the-store.chinese.md index a0ebc3c313..4f99de8b51 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/send-action-data-to-the-store.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/send-action-data-to-the-store.chinese.md @@ -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 -
到目前为止,您已经学会了如何将操作分派给Redux存储,但到目前为止,这些操作还没有包含除type之外的任何信息。您还可以发送特定数据以及您的操作。事实上,这是非常常见的,因为动作通常源于一些用户交互,并倾向于携带一些数据。 Redux商店经常需要了解这些数据。
+
+到目前为止,你已经学会了如何将 action dispatch 给 Redux store,但到目前为止,这些 action 并未包含除 type之外的任何信息。你还可以发送特定数据和 action 一起。事实上,这是非常常见的,因为 action 通常源于一些用户交互,并且往往会携带一些数据,Redux store 经常需要知道这些数据。 +
## Instructions -
在代码编辑器中定义了一个基本的notesReducer()和一个addNoteText()动作创建器。完成addNoteText()函数的主体,以便它返回一个action对象。该对象应包含值为ADD_NOTEtype属性,以及设置为传递给action creator的note数据的text属性。当您致电操作创建者时,您将传递可以访问该对象的特定注释信息。接下来,完成在notesReducer()编写switch语句。您需要添加一个处理addNoteText()操作的案例。只要存在类型为ADD_NOTE的操作,就应该触发此情况,并且应该将传入actiontext属性作为新state 。该操作将在代码底部发送。完成后,运行代码并观察控制台。这就是将特定于操作的数据发送到商店并在更新存储state时使用它所需的全部内容。
+
+在代码编辑器中定义了一个基础的notesReducer()addNoteText() action creator。完成addNoteText()函数的主体,这样它就会返回一个action对象。该对象应该包含一个type属性,其值为ADD_NOTE,还有一个text属性通过 action creator 将值设置为note。当你调用 action creator 时,你需要传入可以访问该对象的特定注释信息。 +接下来,完成在notesReducer()中编写的switch语句。你需要添加一个处理addNoteText()操作的选项。只要存在ADD_NOTE类型的 action,就应该触发 case,并且它应该在传入的action上返回text属性作为新的state +这个 action 将在代码底部发送。一旦完成后,运行代码并观察控制台。这就是将特定于 action 的数据发送到 store 并在更新 store state时使用它所需的全部内容。 +
## Tests
```yml tests: - - text: 动作创建者addNoteText应返回具有键typetext的对象。 + - text: action creator addNoteText应该返回一个包含typetext的对象。 testString: assert((function() { const addNoteFn = addNoteText('__TEST__NOTE'); return addNoteFn.type === ADD_NOTE && addNoteFn.text === '__TEST__NOTE' })()); - - text: 使用addNoteText操作创建程序调度ADD_NOTE类型的操作应将state更新为传递给操作创建者的字符串。 + - text: dispatch 一个 action creator 是addNoteText的actionADD_NOTE,应将state更新为 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()); - ``` @@ -69,7 +74,33 @@ console.log(store.getState());
```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> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/use-a-switch-statement-to-handle-multiple-actions.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/use-a-switch-statement-to-handle-multiple-actions.chinese.md index c61ece2f11..7f37d6a8ff 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/use-a-switch-statement-to-handle-multiple-actions.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/use-a-switch-statement-to-handle-multiple-actions.chinese.md @@ -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 -
您可以告诉Redux商店如何处理多种操作类型。假设您在Redux商店中管理用户身份验证。您希望在用户登录和注销时具有状态表示。您使用经过authenticated的属性的单个状态对象来表示它。您还需要动作创建者创建与用户登录和用户注销相对应的操作,以及操作对象本身。
+
+你可以定义 Redux store 如何处理多种 action 类型。比如你正在 Redux store 中进行用户身份验证,如果你希望用户在登录和注销时具有状态的响应,你可以使用具有authenticated属性的单个的 state 对象。你还需要使用 action creators 创建与用户登录和用户注销相对应的 action,以及 action 对象本身。 +
## Instructions -
代码编辑器为您设置了商店,操作和操作创建器。填写reducer函数以处理多个身份验证操作。在reducer使用JavaScript switch语句来响应不同的操作事件。这是编写Redux减速器的标准模式。 switch语句应该切换action.type并返回适当的身份验证状态。 注意:此时,不要担心状态不变性,因为在这个例子中它很小而且简单。对于每个操作,您可以返回一个新对象 - 例如, {authenticated: true} 。另外,不要忘记在switch语句中写一个返回当前statedefault大小写。这很重要,因为一旦您的应用程序有多个Reducer,它们都会在执行操作调度时运行,即使操作与该reducer无关。在这种情况下,您需要确保返回当前state
+
+代码编辑器为你创建了 store、actions、action creators。通过编写reducer函数来处理多个身份验证操作。可以在reducer通过使用 JavaScript 的switch来响应不同的 action 事件。这是编写 Redux reducer 时的标准模式,switch 语句选择action.type中的一个值并返回相应的身份验证状态。 +注意: 此时,不要担心 state 的不变性,因为在这个示例中它很小而且很简单。所以对于每个操作你都可以返回一个新对象,比如{authenticated:true}。另外,不要忘记在 switch 语句中写一个default case,返回当前的state。这是很重要的,因为一旦你的程序有多个 reducer,当一个 action 被 dispatch 时它们都会运行,即使 action 与该 reducer 无关。在这种情况下,你要确保返回当前的state +
## Tests
```yml tests: - - text: 调用函数loginUser应该返回一个对象,其type属性设置为字符串LOGIN 。 + - text: '调用loginUser函数应该返回一个 {type:"LOGIN"} 对象。' testString: assert(loginUser().type === 'LOGIN'); - - text: 调用函数logoutUser应该返回一个对象,其type属性设置为字符串LOGOUT 。 + - text: '调用logoutUser函数应该返回一个 {type:"LOGOUT"} 对象。' testString: assert(logoutUser().type === 'LOGOUT'); - - text: 应使用经过authenticated属性设置为false的对象初始化存储。 + - text: 'store 应该设置一个初始化对象 {authenticated:false}。' testString: assert(store.getState().authenticated === false); - - text: 调度loginUser应该将store状态中的authenticated属性更新为true 。 + - text: dispatch loginUser应该将 store 中的 state 的authenticated值更新为true。 testString: assert((function() { const initialState = store.getState(); store.dispatch(loginUser()); const afterLogin = store.getState(); return initialState.authenticated === false && afterLogin.authenticated === true })()); - - text: 调度logoutUser应将store状态中的authenticated属性更新为false 。 + - text: dispatch logoutUser应该将 store 中的 state 的authenticated值更新为false。 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: authReducer函数应该使用switch语句处理多个动作类型。 + - text: authReducer函数应该使用switch语句处理多个 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' } }; - ``` @@ -76,8 +80,46 @@ const logoutUser = () => { ## 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> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/use-const-for-action-types.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/use-const-for-action-types.chinese.md index 629934c85b..2ce00f0443 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/use-const-for-action-types.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/use-const-for-action-types.chinese.md @@ -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 -
使用Redux时的一种常见做法是将操作类型指定为只读常量,然后在使用它们的任何地方引用这些常量。您可以重构您正在使用的代码,将操作类型编写为const声明。
+
+在使用 Redux 时的一个常见做法是将操作类型指定为只读,然后在任何使用它们的地方引用这些常量。你可以通过将 action types 使用const声明重构你正在使用的代码。 +
## Instructions -
LOGINLOGOUT声明为const值,并将它们分别分配给字符串'LOGIN''LOGOUT' 。然后,编辑authReducer()和动作创建者以引用这些常量而不是字符串值。 注意:通常以全部大写形式写常量,这也是Redux中的标准做法。
+
+将LOGINLOGOUT声明为const类型的值,并为它们分别分配字符串'LOGIN''LOGOUT'。然后,编辑authReducer()和 action creators 来引用这些常量而不是字符串值。 +注意: 通常以全部大写形式写出常量,这也是 Redux 的标准做法。 +
## Tests
```yml tests: - - text: 调用函数loginUser应该返回一个对象,其type属性设置为字符串LOGIN 。 + - text: '调用loginUser函数应该返回一个 {type:"LOGIN"} 对象。' testString: assert(loginUser().type === 'LOGIN'); - - text: 调用函数logoutUser应该返回一个对象,其type属性设置为字符串LOGOUT 。 + - text: '调用logoutUser函数应该返回一个 {type:\"LOGOUT\"} 对象。' testString: assert(logoutUser().type === 'LOGOUT'); - - text: 应使用属性login设置为false的对象初始化存储。 + - text: store 应该初始化一个对象 {login:false}。 testString: assert(store.getState().authenticated === false); - - text: 调度loginUser应该将store状态中的login属性更新为true 。 + - text: dispatch loginUser以后应将 store 中的 state 的login值更新为true。 testString: assert((function() { const initialState = store.getState(); store.dispatch(loginUser()); const afterLogin = store.getState(); return initialState.authenticated === false && afterLogin.authenticated === true })()); - - text: 调度logoutUser应将store状态中的login属性更新为false 。 + - text: dispatch logoutUser应将 store 中的 state 的login值更新为false。 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: authReducer函数应该使用switch语句处理多个动作类型。 + - text: authReducer函数应该使用 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: LOGINLOGOUT应声明为const值,并应分配LOGINLOGOUT字符串。 - 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: 动作创建者和减速器应该引用LOGINLOGOUT常量。 + - text: '应该使用const LOGIN="LOGIN"const LOGOUT="LOGOUT"的方式声明LOGINLOGOUT。' + testString: const noWhiteSpace = code.replace(/\s/g, ''); assert(/constLOGIN=(['"`])LOGIN\1/.test(noWhiteSpace) && /constLOGOUT=(['"`])LOGOUT\1/.test(noWhiteSpace)); + - text: action creator 和 reducer 中应该引用LOGINLOGOUT常量。 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:
```jsx -// change code below this line +// 修改此行下方的代码 -// change code above this line +// 修改此行上方的代码 const defaultState = { authenticated: false @@ -87,7 +92,6 @@ const logoutUser = () => { type: 'LOGOUT' } }; - ```
@@ -99,8 +103,49 @@ const logoutUser = () => { ## 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> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/use-middleware-to-handle-asynchronous-actions.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/use-middleware-to-handle-asynchronous-actions.chinese.md index a38ac2096d..51bda524c2 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/use-middleware-to-handle-asynchronous-actions.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/use-middleware-to-handle-asynchronous-actions.chinese.md @@ -3,30 +3,37 @@ id: 5a24c314108439a4d4036156 title: Use Middleware to Handle Asynchronous Actions challengeType: 6 isRequired: false -videoUrl: '' +forumTopicId: 301451 localeTitle: 使用中间件处理异步操作 --- ## Description -
到目前为止,这些挑战已经避免讨论异步操作,但它们是Web开发中不可避免的一部分。在某些时候,您需要在Redux应用程序中调用异步端点,那么如何处理这些类型的请求? Redux提供专门为此目的而设计的中间件,称为Redux Thunk中间件。以下是如何在Redux中使用它的简要说明。要包含Redux Thunk中间件,请将其作为参数传递给Redux.applyMiddleware() 。然后,此语句作为createStore()函数的第二个可选参数提供。看一下编辑器底部的代码,看看这个。然后,要创建异步操作,您将在动作创建器中返回一个函数,该函数将dispatch作为参数。在此函数中,您可以分派操作并执行异步请求。在此示例中,使用setTimeout()调用模拟异步请求。在启动任何异步行为之前调度操作是很常见的,这样您的应用程序状态就知道正在请求某些数据(例如,此状态可能会显示加载图标)。然后,一旦收到数据,就会发送另一个操作,该操作将数据作为有效负载以及操作完成的信息。请记住,您将dispatch作为参数传递给此特殊操作创建者。这是您用于分派操作的方法,您只需将操作直接传递给调度,中间件就可以处理其余操作。
+
+目前为止的挑战都在避免讨论异步操作,但它们是 Web 开发中不可避免的一部分。在某些时候,你需要在 Redux 应用程序中使用异步请求,那么如何处理这些类型的请求?Redux 中间件专为此目的而设计,称为 Redux Thunk 中间件。这里简要介绍如何在 Redux 中使用它。 +如果要使用 Redux Thunk 中间件,请将其作为参数传递给Redux.applyMiddleware()。然后将此函数作为第二个可选参数提供给createStore()函数,看一下编辑器底部的代码,然后,要创建一个异步的 action,你需要在 action creator 中返回一个以dispatch为参数的函数。在这个函数中,你可以 dispatch action 并执行异步请求。 +在此示例中,使用setTimeout()调用模拟异步请求。通常在执行异步行为之前 dispatch action,以便应用程序状态知道正在请求某些数据(例如,这个状态可以显示加载图标)。然后,一旦收到数据,就会发送另一个 action,这个 action 完成的时间将作为数据的一个有效值。 +请记住,你正在通过将dispatch作为参数传递给这个特殊的 action creator。你用于 dispatch 你的 action 时只需将 action 直接传递给 dispatch,中间件就可以处理其余的部分。 +
## Instructions -
handleAsync()动作创建器中写入两个调度。调度requestingData()在之前setTimeout()模拟API调用)。然后,在收到(假装)数据后,调度receivedData()操作,传入此数据。现在您知道如何在Redux中处理异步操作。其他一切继续像以前一样。
+
+在handleAsync()的 action creator 中编写两个 dispatch 事件。在setTimeout()(模拟 API 调用)之前 dispatchrequestingData()。然后在收到(模拟)数据后,dispatchreceivedData()action,传入这些数据。现在你知道如何处理 Redux 中的异步操作,其他所有操作都继续像以前一样。 +
## Tests
```yml tests: - - text: requestingData操作创建者应返回类型等于REQUESTING_DATA值的对象。 + - text: requesData action creator 应返回类型和值都为REQUESTING_DATA的对象。 testString: assert(requestingData().type === REQUESTING_DATA); - - text: receivedData操作创建者应返回类型等于RECEIVED_DATA值的对象。 + - text: receivedData action creator 应返回类型和值都等于RECEIVED_DATA的对象。 testString: assert(receivedData('data').type === RECEIVED_DATA); - - text: asyncDataReducer应该是一个函数。 + - text: asyncDataReducer必须是个函数。 testString: assert(typeof asyncDataReducer === 'function'); - - text: 调度requestedData操作创建者应该将获取的store state属性更新为true 。 + - text: 分发 requestedData 后 action creator 应该将 store 中的state的 fetching 的值更新为 true。 testString: assert((function() { const initialState = store.getState(); store.dispatch(requestingData()); const reqState = store.getState(); return initialState.fetching === false && reqState.fetching === true })()); - - text: 调度handleAsync应调度数据请求操作,然后在延迟后调度接收的数据操作。 + - text: 如果要 dispatch handleAsync 应先 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) ); - ``` @@ -97,8 +103,52 @@ const store = Redux.createStore( ## 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> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/use-the-spread-operator-on-arrays.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/use-the-spread-operator-on-arrays.chinese.md index 512bdfdee0..cc67f32e2d 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/use-the-spread-operator-on-arrays.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/use-the-spread-operator-on-arrays.chinese.md @@ -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 -
ES6的一个解决方案是帮助在Redux中强制执行状态不变性,它是扩展运算符: ...扩展运算符具有各种应用程序,其中一个非常适合于从现有阵列生成新阵列的先前挑战。这是相对较新的,但常用的语法。例如,如果你有一个数组myArray并且写: let newArray = [...myArray]; newArray现在是myArray的克隆。两个阵列仍然分别存在于内存中。如果你执行像newArray.push(5)这样的变异, myArray不会改变。 ...有效地 myArray的值展开到一个新数组中。要克隆数组但在新数组中添加其他值,可以编写[...myArray, 'new value'] 。这将返回一个由myArray中的值和字符串'new value'组成的新数组作为最后一个值。扩展语法可以像这样在数组组合中多次使用,但重要的是要注意它只是生成数组的浅表副本。也就是说,它只为一维数组提供不可变的数组操作。
+
+ES6 中有助于在 Redux 中强制执行状态不变性的一个解决方案是扩展运算符:...。扩展运算符具有很多的应用,其中一种非常适合通过一个已有的数组生成一个新数组。这是相对较新的,但常用的语法。例如,如果你有一个数组myArray并写: +let newArray = [...myArray]; +newArray现在是myArray的克隆。两个数组仍然在内存中单独存在。如果你执行像newArray.push(5)这样的突变,myArray不会改变。...有效myArray中的值传播到新数组中。要克隆数组但在新数组中添加其他值,可以编写[... myArray,'new value']。这将返回一个由中的值组成的新数组。myArray和字符串'new value'作为最后一个值。扩展语法可以像这样在数组组合中多次使用,但重要的是要注意它只做一个浅拷贝这就是说,它只为一维数组提供了不可变的数组操作。 +
## Instructions -
添加待办事项时,使用spread运算符返回新的状态副本。
+
+添加待办事项时,使用 spread 运算符返回新的状态副本。 +
## Tests
```yml tests: - - text: 'Redux存储应该存在并初始化为等于[Do not mutate state!] 。' + - text: Redux store 应该存在并初始化一个[Do not mutate state!]的状态。 testString: assert((function() { const initialState = store.getState(); return ( Array.isArray(initialState) === true && initialState[0] === 'Do not mutate state!'); })()); - - text: addToDoimmutableReducer都应该是函数。 + - text: addToDoimmutableReducer都应该是一个函数。 testString: assert(typeof addToDo === 'function' && typeof immutableReducer === 'function'); - - text: 在Redux存储上调度ADD_TO_DO类型的操作应该添加todo ,不应该改变状态。 + - text: 在 Redux store 上 dispatch 一个类型为ADD_TO_DO aciton 应该添加一个todo项,并且不应该改变 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); - ``` @@ -67,8 +72,28 @@ const store = Redux.createStore(immutableReducer); ## 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> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/redux/write-a-counter-with-redux.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/redux/write-a-counter-with-redux.chinese.md index 8bb37a224e..1d87eb44dd 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/redux/write-a-counter-with-redux.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/redux/write-a-counter-with-redux.chinese.md @@ -3,32 +3,36 @@ id: 5a24c314108439a4d4036157 title: Write a Counter with Redux challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 用Redux写一个计数器 +forumTopicId: 301453 +localeTitle: 用 Redux 写一个计数器 --- ## Description -
现在您已经了解了Redux的所有核心原则!您已经了解了如何创建操作和操作创建器,创建Redux存储,针对存储分派操作以及使用纯reducer设计状态更新。您甚至已经了解了如何使用reducer组合管理复杂状态并处理异步操作。这些示例过于简单,但这些概念是Redux的核心原则。如果您理解它们,那么您已准备好开始构建自己的Redux应用程序。接下来的挑战涵盖了有关state不变性的一些细节,但首先,这里是对迄今为止所学到的所有内容的回顾。
+
+现在你已经了解了 Redux 的所有核心原则!你已经了解了如何创建 action 和 action creator,创建 Redux store,通过 store dispatch action,以及使用纯粹的 reducer 设计状态更新。你甚至已经看到过如何使用 reducer 组合管理复杂状态并处理异步操作。这些例子很简单,但这些概念是 Redux 的核心原则。如果你理解它们,你就可以开始构建自己的 Redux 应用了。接下来的挑战包括关于state不变性的一些细节,但是,这里是对你到目前为止学到的所有内容的回顾。 +
## Instructions -
在本课程中,您将从头开始使用Redux实现一个简单的计数器。代码编辑器中提供了基础知识,但您必须填写详细信息!使用提供的名称并定义incActiondecAction操作创建者, decAction counterReducer()INCREMENTDECREMENT操作类型,最后定义Redux store 。一旦完成,您应该能够发送INCREMENTDECREMENT操作来增加或减少store保存的状态。祝你好运第一个Redux应用程序!
+
+在本课程中,你将从头开始使用 Redux 实现一个简单的计数器。基本知识在代码编辑器中提供,但你必须完成详细的内容!使用提供给你的名称并定义incActiondecActio action creator counterReducer()INCREMENTDECREMENT action 类型,最后是 Redux store。一旦完成,你应该能够 dispatch INCREMENTDECREMENT动作来增加或减少store中保存的状态。开始构建你的第一个 Redux 应用程序吧,祝你好运! +
## Tests
```yml tests: - - text: 动作创建者incAction应返回type等于INCREMENT值的动作对象 + - text: 'action creator incAction应返回一个 action 对象 {type:"INCREMENT"}。' testString: assert(incAction().type ===INCREMENT); - - text: 动作创建者decAction应与返回动作对象type等于的值DECREMENT + - text: 'action creator decAction应返回一个 action 对象 {type:"DECREMENT"}。' testString: assert(decAction().type === DECREMENT); - - text: Redux存储应该以0 state初始化。 + - text: Redux store 应该将state初始化为 0。 testString: assert(store.getState() === 0); - - text: 在Redux存储上调度incAction应该将state增加1。 + - text: 在 Redux store 上 dispatch incAction应该将state增加 1。 testString: assert((function() { const initialState = store.getState(); store.dispatch(incAction()); const incState = store.getState(); return initialState + 1 === incState })()); - - text: 在Redux存储上调度decAction应该将state减1。 + - text: 在 Redux store 上 dispatch decAction应该将state减少 1。 testString: assert((function() { const initialState = store.getState(); store.dispatch(decAction()); const decState = store.getState(); return initialState - 1 === decState })()); - - text: counterReducer应该是一个函数 + - text: counterReducer必须是一个函数。 testString: assert(typeof counterReducer === 'function'); ``` @@ -41,17 +45,16 @@ tests:
```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 ```
@@ -63,8 +66,35 @@ const store = null; // define the Redux store here, passing in your reducers ## 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> +