feat: add 'back/front end' in curriculum (#42596)

* chore: rename APIs and Microservices to include "Backend" (#42515)

* fix typo

* fix typo

* undo change

* Corrected grammar mistake

Corrected a grammar mistake by removing a comma.

* change APIs and Microservices cert title

* update title

* Change APIs and Microservices certi title

* Update translations.json

* update title

* feat(curriculum): rename apis and microservices cert

* rename folder structure

* rename certificate

* rename learn Markdown

* apis-and-microservices -> back-end-development-and-apis

* update backend meta

* update i18n langs and cypress test

Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>

* fix: add development to front-end libraries (#42512)

* fix: added-the-word-Development-to-front-end-libraries

* fix/added-the-word-Development-to-front-end-libraries

* fix/added-word-development-to-front-end-libraries-in-other-related-files

* fix/added-the-word-Development-to-front-end-and-all-related-files

* fix/removed-typos-from-last-commit-in-index.md

* fix/reverted-changes-that-i-made-to-dependecies

* fix/removed xvfg

* fix/reverted changes that i made to package.json

* remove unwanted changes

* front-end-development-libraries changes

* rename backend certSlug and README

* update i18n folder names and keys

* test: add legacy path redirect tests

This uses serve.json from the client-config repo, since we currently use
that in production

* fix: create public dir before moving serve.json

* fix: add missing script

* refactor: collect redirect tests

* test: convert to cy.location for stricter tests

* rename certificate folder to 00-certificates

* change crowdin config to recognise new certificates location

* allow translations to be used

Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com>

* add forwards slashes to path redirects

* fix cypress path tests again

* plese cypress

* fix: test different challenge

Okay so I literally have no idea why this one particular challenge
fails in Cypress Firefox ONLY. Tom and I paired and spun a full build
instance and confirmed in Firefox the page loads and redirects as
expected. Changing to another bootstrap challenge passes Cypress firefox
locally. Absolutely boggled by this.

AAAAAAAAAAAAAAA

* fix: separate the test

Okay apparently the test does not work unless we separate it into
a different `it` statement.

>:( >:( >:( >:(

Co-authored-by: Sujal Gupta <55016909+heysujal@users.noreply.github.com>
Co-authored-by: Noor Fakhry <65724923+NoorFakhry@users.noreply.github.com>
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com>
This commit is contained in:
Shaun Hamilton
2021-08-14 03:57:13 +01:00
committed by GitHub
parent 4df2a0c542
commit c2a11ad00d
1215 changed files with 790 additions and 449 deletions

View File

@@ -0,0 +1,175 @@
---
id: 5a24c314108439a4d4036154
title: Combinar múltiplos reducers
challengeType: 6
forumTopicId: 301436
dashedName: combine-multiple-reducers
---
# --description--
Quando o estado do seu app começa a se tornar mais complexo, pode ser tentador dividir o estado em várias partes. Em vez disso, lembre-se do primeiro princípio do Redux: todos os estados do aplicativo são mantidos em um único objeto de estado na store. Portanto, Redux fornece a composição do redutor como solução para um modelo de estado complexo. Você define vários reducers para lidar com diferentes partes do estado da sua aplicação, e então compõe estes reducers em um único reducer raiz. O reducer raiz é então passado para o método Redux `createStore()`.
Para que possamos combinar múltiplos reducers juntos, o Redux fornece o método `combineReducers()`. Este método aceita um objeto como argumento no qual você define propriedades que associam chaves a funções específicas de reducer. Os nomes que você der às chaves serão usados pelo Redux para pedaço do nome associado do state.
Normalmente, é uma boa prática criar um reducer para cada peça do estado da aplicação quando eles são distintos ou únicos de alguma forma. Por exemplo, em um aplicativo de anotações com autenticação de usuário, um reducer poderia lidar com a autenticação enquanto outro manipula o texto e notas que o usuário está enviando. Para tal aplicação, podemos escrever o método `combineReducers()` assim:
```js
const rootReducer = Redux.combineReducers({
auth: authenticationReducer,
notes: notesReducer
});
```
Agora, as chaves `notes` conterão todo o estado associado às nossas notas e serão tratadas pelo nosso `notesReducer`. É assim que múltiplos reducers podem ser compostos para gerenciar um estado mais complexo da aplicação. Neste exemplo, o estado mantido no armazenamento do Redux seria, em seguida, um único objeto contendo as propriedades `auth` e `notes`.
# --instructions--
Existem as funções `counterReducer()` e `authReducer()` fornecidas no editor de código, junto com uma store do Redux. Termine de escrever a função `rootReducer()` usando o método `Redux.combineReducers()`. Atribua `counterReducer` a uma chave chamada `count` e `authReducer` a uma chave chamada `auth`.
# --hints--
O `counterReducer` deve incrementar e decrementar o `state`.
```js
assert(
(function () {
const initialState = store.getState().count;
store.dispatch({ type: INCREMENT });
store.dispatch({ type: INCREMENT });
const firstState = store.getState().count;
store.dispatch({ type: DECREMENT });
const secondState = store.getState().count;
return firstState === initialState + 2 && secondState === firstState - 1;
})()
);
```
O `authReducer` deve alternar o `state` de `authenticated` entre `true` e `false`.
```js
assert(
(function () {
store.dispatch({ type: LOGIN });
const loggedIn = store.getState().auth.authenticated;
store.dispatch({ type: LOGOUT });
const loggedOut = store.getState().auth.authenticated;
return loggedIn === true && loggedOut === false;
})()
);
```
O `state` armazenado deve ter duas chaves: `count`, que contém um número e um `auth`, que contém um objeto. O objeto `auth` deve ter a propriedade `authenticated`, a qual possui um booleano.
```js
assert(
(function () {
const state = store.getState();
return (
typeof state.auth === 'object' &&
typeof state.auth.authenticated === 'boolean' &&
typeof state.count === 'number'
);
})()
);
```
`rootReducer` deve ser uma função que combina o `counterReducer` e o `authReducer`.
```js
(getUserInput) =>
assert(
(function () {
const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index'));
return (
typeof rootReducer === 'function' &&
noWhiteSpace.includes('Redux.combineReducers')
);
})()
);
```
# --seed--
## --seed-contents--
```js
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const counterReducer = (state = 0, action) => {
switch(action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
};
const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';
const authReducer = (state = {authenticated: false}, action) => {
switch(action.type) {
case LOGIN:
return {
authenticated: true
}
case LOGOUT:
return {
authenticated: false
}
default:
return state;
}
};
const rootReducer = // Define the root reducer here
const store = Redux.createStore(rootReducer);
```
# --solutions--
```js
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const counterReducer = (state = 0, action) => {
switch(action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
};
const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';
const authReducer = (state = {authenticated: false}, action) => {
switch(action.type) {
case LOGIN:
return {
authenticated: true
}
case LOGOUT:
return {
authenticated: false
}
default:
return state;
}
};
const rootReducer = Redux.combineReducers({
count: counterReducer,
auth: authReducer
});
const store = Redux.createStore(rootReducer);
```

View File

@@ -0,0 +1,133 @@
---
id: 5a24c314108439a4d403615b
title: Copiar um objeto com Object.assign
challengeType: 6
forumTopicId: 301437
dashedName: copy-an-object-with-object-assign
---
# --description--
Os últimos desafios funcionaram com arrays, mas também existem maneiras de ajudar a impor a imutabilidade do state quando o state for um `object`. Uma ferramenta útil para lidar com objetos é o `Object.assign()`. `Object.assign()` recebe um objeto alvo e objetos fonte e mapeia propriedades dos objetos de origem para o objeto alvo. Qualquer propriedades correspondentes são substituídas por propriedades nos objetos de origem. Esse comportamento é comumente usado para fazer cópias rasas de objetos, passando um objeto vazio como o primeiro argumento seguido do(s) objeto(s) que você deseja copiar. Exemplo:
```js
const newObject = Object.assign({}, obj1, obj2);
```
Isso cria `newObject` como um novo `object`, que contém as propriedades que atualmente existem em `obj1` e `obj2`.
# --instructions--
O estado e ações do Redux foram modificados para manipular um `object` para o `state`. Edite o código para retornar um novo objeto `state` para ações com tipo `ONLINE`, que define a propriedade `status` para a string `online`. Tente usar `Object.assign()` para completar o desafio.
# --hints--
A store do Redux deve existir e inicializar com um estado equivalente ao objeto `defaultState` declarado na linha 1.
```js
assert(
(function () {
const expectedState = {
user: 'CamperBot',
status: 'offline',
friends: '732,982',
community: 'freeCodeCamp'
};
const initialState = store.getState();
return DeepEqual(expectedState, initialState);
})()
);
```
`wakeUp` e `immutableReducer` devem ser funções.
```js
assert(typeof wakeUp === 'function' && typeof immutableReducer === 'function');
```
Despachar uma ação do tipo `ONLINE` deve atualizar a propriedade `status` no estado para `online` e NÃO deve alterar o estado.
```js
assert(
(function () {
const initialState = store.getState();
const isFrozen = DeepFreeze(initialState);
store.dispatch({ type: 'ONLINE' });
const finalState = store.getState();
const expectedState = {
user: 'CamperBot',
status: 'online',
friends: '732,982',
community: 'freeCodeCamp'
};
return isFrozen && DeepEqual(finalState, expectedState);
})()
);
```
`Object.assign` deve ser usado para retornar um novo estado.
```js
(getUserInput) => assert(getUserInput('index').includes('Object.assign'));
```
# --seed--
## --seed-contents--
```js
const defaultState = {
user: 'CamperBot',
status: 'offline',
friends: '732,982',
community: 'freeCodeCamp'
};
const immutableReducer = (state = defaultState, action) => {
switch(action.type) {
case 'ONLINE':
// Don't mutate state here or the tests will fail
return
default:
return state;
}
};
const wakeUp = () => {
return {
type: 'ONLINE'
}
};
const store = Redux.createStore(immutableReducer);
```
# --solutions--
```js
const defaultState = {
user: 'CamperBot',
status: 'offline',
friends: '732,982',
community: 'freeCodeCamp'
};
const immutableReducer = (state = defaultState, action) => {
switch(action.type) {
case 'ONLINE':
return Object.assign({}, state, {
status: 'online'
});
default:
return state;
}
};
const wakeUp = () => {
return {
type: 'ONLINE'
}
};
const store = Redux.createStore(immutableReducer);
```

View File

@@ -0,0 +1,61 @@
---
id: 5a24c314108439a4d403614b
title: Criar uma store do Redux
challengeType: 6
forumTopicId: 301439
dashedName: create-a-redux-store
---
# --description--
Redux é um framework de gerenciamento do state que pode ser usada com uma série de diferentes tecnologias web, incluindo React.
No Redux, há um único objeto state responsável por todo o estado de sua aplicação. Isto significa que se você tinha um aplicativo React com dez componentes, e cada componente tinha seu próprio estado local, todo o estado do seu aplicativo seria definido por um único objeto de estado alojado na `store` do Redux. Este é o primeiro princípio importante a ser entendido ao aprender Redux: o armazenamento do Redux é a única fonte da verdade quando se trata do estado do aplicativo.
Isso também significa que, a qualquer momento que qualquer parte de seu aplicativo queira atualizar state, **deve** fazer isso através do store do Redux. O fluxo de dados unidirecional torna mais fácil rastrear o gerenciamento de estado em seu aplicativo.
# --instructions--
O `store` do Redux é um objeto que contém e gerencia o `state` da aplicação. Há um método chamado `createStore()` no objeto do Redux, que você usa para criar a `store` do Redux. Este método recebe uma função `reducer` como um argumento necessário. A função `reducer` é coberta em um desafio posterior, e já está definida para você no editor de código. Ele simplesmente recebe o `state` como um argumento e retorna o `state`.
Declare uma variável `store` e atribua-a ao método `createStore()`, passando no `reducer` como argumento.
**Observação:** o código no editor usa a sintaxe de argumento padrão ES6 para inicializar este estado para ter um valor de `5`. Se você não está familiarizado com os argumentos padrão, você pode se referir [ à seção sobre ES6 do currículo](https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/es6/set-default-parameters-for-your-functions) que aborda este tópico.
# --hints--
A store do Redux deve existir.
```js
assert(typeof store.getState === 'function');
```
A store do Redux deve ter um valor de 5 para o estado.
```js
assert(store.getState() === 5);
```
# --seed--
## --seed-contents--
```js
const reducer = (state = 5) => {
return state;
}
// Redux methods are available from a Redux object
// For example: Redux.createStore()
// Define the store here:
```
# --solutions--
```js
const reducer = (state = 5) => {
return state;
}
const store = Redux.createStore(reducer);
```

View File

@@ -0,0 +1,55 @@
---
id: 5a24c314108439a4d403614d
title: Definir uma ação do Redux
challengeType: 6
forumTopicId: 301440
dashedName: define-a-redux-action
---
# --description--
Como o Redux é um framework de gestão de estado, atualizar o state é uma das suas principais tarefas. No Redux, todas as atualizações de estado são acionadas despachando ações. Uma ação é simplesmente um objeto JavaScript que contém informações sobre um evento de ação que ocorreu. A store do Redux recebe estes objetos de ação, então atualiza seu estado de acordo. Às vezes, uma ação do Redux também contém alguns dados. Por exemplo, a ação carrega um nome de usuário após o login do usuário. Enquanto os dados são opcionais, ações devem carregar uma propriedade `type` que especifica o 'tipo' da ação que ocorreu.
Pense em ações Redux como mensageiros que fornecem informações sobre eventos acontecendo em seu aplicativo para a store do Redux. A store então conduz a tarefa de atualizar o state baseado na ação que ocorreu.
# --instructions--
Escrever uma ação Redux é tão simples quanto declarar um objeto com uma propriedade type. Declare um objeto `action` e o dê uma propriedade `type` definido para a string `'LOGIN'`.
# --hints--
Um objeto de `action` deve existir.
```js
assert(
(function () {
return typeof action === 'object';
})()
);
```
O objeto `action` deve ter a propriedade chave `type` com o valor de `LOGIN`.
```js
assert(
(function () {
return action.type === 'LOGIN';
})()
);
```
# --seed--
## --seed-contents--
```js
// Define an action here:
```
# --solutions--
```js
const action = {
type: 'LOGIN'
}
```

View File

@@ -0,0 +1,57 @@
---
id: 5a24c314108439a4d403614e
title: Definir um criador de action
challengeType: 6
forumTopicId: 301441
dashedName: define-an-action-creator
---
# --description--
Após criar uma ação, o próximo passo é enviar a ação para o armazenamento do Redux para que ele possa atualizar seu estado. No Redux, você define os criadores de ação para realizar isso. Um criador de ação é simplesmente uma função JavaScript que retorna uma ação. Em outras palavras, criadores de ações criam objetos que representam eventos de ação.
# --instructions--
Defina uma função chamada `actionCreator()` que retorna o objeto `action` quando chamado.
# --hints--
A função `actionCreator` deve existir.
```js
assert(typeof actionCreator === 'function');
```
Executando a função `actionCreator` deve retornar o objeto `action`.
```js
assert(typeof action === 'object');
```
O objeto `action` deve ter a propriedade chave `type` com o valor `LOGIN`.
```js
assert(action.type === 'LOGIN');
```
# --seed--
## --seed-contents--
```js
const action = {
type: 'LOGIN'
}
// Define an action creator here:
```
# --solutions--
```js
const action = {
type: 'LOGIN'
}
const actionCreator = () => {
return action;
};
```

View File

@@ -0,0 +1,85 @@
---
id: 5a24c314108439a4d403614f
title: Despachar um evento de ação
challengeType: 6
forumTopicId: 301442
dashedName: dispatch-an-action-event
---
# --description--
O método `dispatch` é o que você usa para enviar ações para a store do Redux. Chamar `store.dispatch()` e passar o valor retornado de um criador de ação envia uma ação de volta para a store.
Lembre-se de que os criadores de ações retornam um objeto com uma propriedade type que especifica a ação que ocorreu. Em seguida, o método despacha um objeto de ação para a store do Redux. Com base no exemplo do desafio anterior, as linhas a seguir são equivalentes, e ambas enviam a ação do tipo `LOGIN`:
```js
store.dispatch(actionCreator());
store.dispatch({ type: 'LOGIN' });
```
# --instructions--
A store do Redux no editor de código tem um estado inicializado que contém um objeto que contém a propriedade `login` atualmente definida como `false`. Também há um criador de ação chamado `loginAction()` que retorna uma ação do tipo `LOGIN`. Despache a ação `LOGIN` para a store do Redux chamando o método `dispatch`, e passe na ação criada por `loginAction()`.
# --hints--
Chamando a função `loginAction` deve retornar um objeto com a propriedade `type` definida para a string `LOGIN`.
```js
assert(loginAction().type === 'LOGIN');
```
A store deve ser inicializada com um objeto com a propriedade `login` definido como `false`.
```js
assert(store.getState().login === false);
```
O método `store.dispatch()` deve ser usado para despachar uma ação do tipo `LOGIN`.
```js
(getUserInput) =>
assert(
(function () {
let noWhiteSpace = getUserInput('index').replace(/\s/g, '');
return (
noWhiteSpace.includes('store.dispatch(loginAction())') ||
noWhiteSpace.includes("store.dispatch({type: 'LOGIN'})") === true
);
})()
);
```
# --seed--
## --seed-contents--
```js
const store = Redux.createStore(
(state = {login: false}) => state
);
const loginAction = () => {
return {
type: 'LOGIN'
}
};
// Dispatch the action here:
```
# --solutions--
```js
const store = Redux.createStore(
(state = {login: false}) => state
);
const loginAction = () => {
return {
type: 'LOGIN'
}
};
store.dispatch(loginAction());
```

View File

@@ -0,0 +1,55 @@
---
id: 5a24c314108439a4d403614c
title: Obter o estado na store do Redux
challengeType: 6
forumTopicId: 301443
dashedName: get-state-from-the-redux-store
---
# --description--
O objeto store do Redux fornece vários métodos que permitem que você interaja com ele. Por exemplo, você pode recuperar o `state` atual mantido no objeto store do Redux com o método `getState()`.
# --instructions--
O código do desafio anterior é reescrito de forma mais concisa no editor de código. Use `store.getState()` para recuperar o `state` do `store`, e o atribuir a nova variável `currentState`.
# --hints--
A store do Redux deve ter um valor de 5 para o state inicial.
```js
assert(store.getState() === 5);
```
Uma variável `currentState` deve existir e deve ser atribuída ao state atual na store do Redux.
```js
(getUserInput) =>
assert(
currentState === 5 && getUserInput('index').includes('store.getState()')
);
```
# --seed--
## --seed-contents--
```js
const store = Redux.createStore(
(state = 5) => state
);
// Change code below this line
```
# --solutions--
```js
const store = Redux.createStore(
(state = 5) => state
);
// Change code below this line
const currentState = store.getState();
```

View File

@@ -0,0 +1,108 @@
---
id: 5a24c314108439a4d4036150
title: Manipular uma ação na store
challengeType: 6
forumTopicId: 301444
dashedName: handle-an-action-in-the-store
---
# --description--
Depois que uma ação é criada e despachada, a store do Redux precisa saber como responder a essa ação. Esse é o trabalho de uma função `reducer`. Reducers em Redux são responsáveis pelas modificações de estado que ocorrem em resposta a ações. Um `reducer` recebe como argumentos `state` e `action`, e ele sempre retorna um novo `state`. É importante ver que esse é o **único** papel do reducer. Ele não tem efeitos colaterais — nunca chama uma rota de API e nunca tem nenhuma surpresa escondida. O reducer é simplesmente uma função pura que exige os parâmetros state e action, e depois retorna um novo state.
Outro princípio chave em Redux é que o `state` é somente de leitura. Em outras palavras, a função `reducer` deve **sempre** retornar uma nova cópia do `state` e nunca modificar o estado diretamente. Redux não impõe imutabilidade do estado. No entanto, você é responsável por aplicá-la no código de suas funções reducer. Você vai praticar isso em desafios futuros.
# --instructions--
O editor de código tem o exemplo anterior, bem como o início de uma função `reducer` para você. Preencha o corpo da função `reducer` para que se receber uma ação do tipo `'LOGIN'` ele retorna um objeto state com `login` definido como `true`. Caso contrário, retorna o `state` atual. Note que o `state` atual e a `action` despachada foram passados para o reducer, para que você possa acessar o tipo da ação diretamente com `action.type`.
# --hints--
Chamar a função `loginAction` deve retornar um objeto com a propriedade `type` definida para a string `LOGIN`.
```js
assert(loginAction().type === 'LOGIN');
```
A store deve ser inicializada com um objeto com a propriedade `login` definida como `false`.
```js
assert(store.getState().login === false);
```
Despachar `loginAction` deve atualizar a propriedade `login` no state da store para `true`.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(loginAction());
const afterState = store.getState();
return initialState.login === false && afterState.login === true;
})()
);
```
Se a ação não é do tipo `LOGIN`, a store deve retornar o estado atual.
```js
assert(
(function () {
store.dispatch({ type: '__TEST__ACTION__' });
let afterTest = store.getState();
return typeof afterTest === 'object' && afterTest.hasOwnProperty('login');
})()
);
```
# --seed--
## --seed-contents--
```js
const defaultState = {
login: false
};
const reducer = (state = defaultState, action) => {
// Change code below this line
// Change code above this line
};
const store = Redux.createStore(reducer);
const loginAction = () => {
return {
type: 'LOGIN'
}
};
```
# --solutions--
```js
const defaultState = {
login: false
};
const reducer = (state = defaultState, action) => {
if (action.type === 'LOGIN') {
return {login: true}
}
else {
return state
}
};
const store = Redux.createStore(reducer);
const loginAction = () => {
return {
type: 'LOGIN'
}
};
```

View File

@@ -0,0 +1,133 @@
---
id: 5a24c314108439a4d4036158
title: Manter o state inalterado
challengeType: 6
forumTopicId: 301445
dashedName: never-mutate-state
---
# --description--
Estes desafios finais descrevem vários métodos de aplicação do princípio fundamental da imutabilidade do state em Redux. O state imutável significa que você nunca modifica o estado diretamente, em vez disso, você retorna uma nova cópia do estado.
Se você tirasse um snapshot do estado de um aplicativo Redux ao longo do tempo, você veria algo como `state 1`, `state 2`, `state 3`,`state 4`, `...` e assim por diante onde cada estado pode ser semelhante ao último, mas cada um é um dado distinto. Esta imutabilidade, na verdade, é o que fornece recursos como a depuração de viagem no tempo (time-travel debugging) sobre o qual você pode ter ouvido a respeito.
Redux não impõe ativamente a imutabilidade do state em sua store ou nos reducers. Essa responsabilidade é do programador. Felizmente, o JavaScript (especialmente ES6) fornece várias ferramentas úteis que você pode usar para impor a imutabilidade do seu estado, seja ele uma `string`, `number`, `array` ou `object`. Observe que as strings e números são valores primitivos e são imutáveis por natureza. Em outras palavras, 3 é sempre 3. Você não pode alterar o valor do número 3. Um `array` ou `object`, no entanto, é mutável. Na prática, seu estado provavelmente consistirá em um `array` ou `object`, visto que se trata de estruturas de dados úteis para a representação de muitos tipos de informação.
# --instructions--
Há uma `store` e um `reducer` no editor de código para gerenciar itens a fazer. Termine de escrever o caso `ADD_TO_DO` no reducer para adicionar um novo 'a fazer' (to-do) ao state. Existem algumas maneiras de realizar isso com JavaScript padrão ou ES6. Veja se você pode encontrar uma maneira de retornar um novo array com o item do `action.todo` anexado ao final.
# --hints--
A store do Redux deve existir e inicializar com um estado igual ao array `todos` no editor de código.
```js
assert(
(function () {
const todos = [
'Go to the store',
'Clean the house',
'Cook dinner',
'Learn to code'
];
const initialState = store.getState();
return (
Array.isArray(initialState) && initialState.join(',') === todos.join(',')
);
})()
);
```
`addToDo` e `immutableReducer` ambas devem ser funções.
```js
assert(typeof addToDo === 'function' && typeof immutableReducer === 'function');
```
Despachar uma ação do tipo `ADD_TO_DO` na store do Redux deve adicionar um item `todo` e NÃO deve modificar o state.
```js
assert(
(function () {
const initialState = store.getState();
const isFrozen = DeepFreeze(initialState);
store.dispatch(addToDo('__TEST__TO__DO__'));
const finalState = store.getState();
const expectedState = [
'Go to the store',
'Clean the house',
'Cook dinner',
'Learn to code',
'__TEST__TO__DO__'
];
return isFrozen && DeepEqual(finalState, expectedState);
})()
);
```
# --seed--
## --seed-contents--
```js
const ADD_TO_DO = 'ADD_TO_DO';
// A list of strings representing tasks to do:
const todos = [
'Go to the store',
'Clean the house',
'Cook dinner',
'Learn to code',
];
const immutableReducer = (state = todos, action) => {
switch(action.type) {
case ADD_TO_DO:
// Don't mutate state here or the tests will fail
return
default:
return state;
}
};
const addToDo = (todo) => {
return {
type: ADD_TO_DO,
todo
}
}
const store = Redux.createStore(immutableReducer);
```
# --solutions--
```js
const ADD_TO_DO = 'ADD_TO_DO';
const todos = [
'Go to the store',
'Clean the house',
'Cook dinner',
'Learn to code',
];
const immutableReducer = (state = todos, action) => {
switch(action.type) {
case ADD_TO_DO:
return state.concat(action.todo);
default:
return state;
}
};
const addToDo = (todo) => {
return {
type: ADD_TO_DO,
todo
}
}
const store = Redux.createStore(immutableReducer);
```

View File

@@ -0,0 +1,118 @@
---
id: 5a24c314108439a4d4036153
title: Registrar um listener de store
challengeType: 6
forumTopicId: 301446
dashedName: register-a-store-listener
---
# --description--
Outro método a que você tem acesso no objeto `store` do Redux é `store.subscribe()`. Isso permite que você inscreva funções de listener à store, que são chamadas sempre que uma ação é enviada à store. Um uso simples para este método é inscrever uma função na sua store que simplesmente registra uma mensagem toda vez que uma ação é recebida e a store é atualizada.
# --instructions--
Escreva uma função de callback que incrementa a variável global `count` toda vez que a store receber uma ação, e passe esta função para o método `store.subscribe()`. Você verá que `store.dispatch()` é chamado três vezes seguidas, a cada vez que passa diretamente em um objeto de ação. Assista a saída do console entre os despachos de ação para ver as atualizações que ocorrem.
# --hints--
Despachar a ação `ADD` na store deve incrementar o estado por `1`.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch({ type: 'ADD' });
const newState = store.getState();
return newState === initialState + 1;
})()
);
```
Deve haver uma função de listener inscrita na store usando `store.subscribe`.
```js
(getUserInput) => assert(getUserInput('index').match(/store\s*\.\s*subscribe\(/gm));
```
O `store.subscribe` deve receber uma função.
```js
(getUserInput) => assert(getUserInput('index').match(/(\s*function\s*)|(\s*\(\s*\)\s*=>)/gm))
```
A função de callback para `store.subscribe` também deve incrementar a variável global `count` à medida que a store é atualizada.
```js
assert(store.getState() === count);
```
# --seed--
## --before-user-code--
```js
count = 0;
```
## --seed-contents--
```js
const ADD = 'ADD';
const reducer = (state = 0, action) => {
switch(action.type) {
case ADD:
return state + 1;
default:
return state;
}
};
const store = Redux.createStore(reducer);
// Global count variable:
let count = 0;
// Change code below this line
// Change code above this line
store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);
```
# --solutions--
```js
const ADD = 'ADD';
const reducer = (state = 0, action) => {
switch(action.type) {
case ADD:
return state + 1;
default:
return state;
}
};
const store = Redux.createStore(reducer);
let count = 0;
// Change code below this line
store.subscribe( () =>
{
count++;
}
);
// Change code above this line
store.dispatch({type: ADD});
store.dispatch({type: ADD});
store.dispatch({type: ADD});
```

View File

@@ -0,0 +1,114 @@
---
id: 5a24c314108439a4d403615a
title: Remover um item de um array
challengeType: 6
forumTopicId: 301447
dashedName: remove-an-item-from-an-array
---
# --description--
Hora de praticar a remoção de itens de um array. O operador spread também pode ser usado aqui. Outros métodos de JavaScript úteis incluem `slice()` e `concat()`.
# --instructions--
O reducer e o criador de ação foram modificados para remover um item de um array com base no índice do item. Termine de escrever o reducer para que um novo array de state seja retornado com o item no índice especificado removido.
# --hints--
A store do Redux deve existir e inicializar com um estado igual a `[0,1,2,3,4,5]`
```js
assert(
(function () {
const initialState = store.getState();
return (
Array.isArray(initialState) === true &&
DeepEqual(initialState, [0, 1, 2, 3, 4, 5])
);
})()
);
```
`removeItem` e `immutableReducer` devem ser funções.
```js
assert(
typeof removeItem === 'function' && typeof immutableReducer === 'function'
);
```
Despachar o criador de ação `removeItem` deve remover itens do state e NÃO deve modificar o state.
```js
assert(
(function () {
const initialState = store.getState();
const isFrozen = DeepFreeze(initialState);
store.dispatch(removeItem(3));
const state_1 = store.getState();
store.dispatch(removeItem(2));
const state_2 = store.getState();
store.dispatch(removeItem(0));
store.dispatch(removeItem(0));
store.dispatch(removeItem(0));
const state_3 = store.getState();
return (
isFrozen &&
DeepEqual(state_1, [0, 1, 2, 4, 5]) &&
DeepEqual(state_2, [0, 1, 4, 5]) &&
DeepEqual(state_3, [5])
);
})()
);
```
# --seed--
## --seed-contents--
```js
const immutableReducer = (state = [0,1,2,3,4,5], action) => {
switch(action.type) {
case 'REMOVE_ITEM':
// Don't mutate state here or the tests will fail
return
default:
return state;
}
};
const removeItem = (index) => {
return {
type: 'REMOVE_ITEM',
index
}
}
const store = Redux.createStore(immutableReducer);
```
# --solutions--
```js
const immutableReducer = (state = [0,1,2,3,4,5], action) => {
switch(action.type) {
case 'REMOVE_ITEM':
return [
...state.slice(0, action.index),
...state.slice(action.index + 1)
];
default:
return state;
}
};
const removeItem = (index) => {
return {
type: 'REMOVE_ITEM',
index
}
}
const store = Redux.createStore(immutableReducer);
```

View File

@@ -0,0 +1,107 @@
---
id: 5a24c314108439a4d4036155
title: Enviar dados de ação para a store
challengeType: 6
forumTopicId: 301448
dashedName: send-action-data-to-the-store
---
# --description--
Até agora você aprendeu como enviar ações para a store do Redux, mas até agora essas ações não continham nenhuma informação diferente de um `type`. Você também pode enviar dados específicos, juntamente com suas ações. Na verdade, isso é muito comum porque as ações geralmente se originam de alguma interação do usuário e tendem a carregar alguns dados com elas. Muitas vezes, a store do Redux precisa saber sobre esses dados.
# --instructions--
Há dois criadores de ações básicos, `notesReducer()` e um `addNoteText()`, definidos no editor de código. Finalize o corpo da função `addNodeText()` para que ela retorne um objeto `action`. O objeto deve incluir a propriedade `type` com o valor `ADD_NOTE` e também uma propriedade `text` definida para o dado `note` que foi passado ao criador de ação. Quando você chama o criador de ação, você passará uma informação note específica que você pode acessar para o objeto.
Em seguida, termine de escreve a instrução `switch` no `notesReducer()`. Você precisa adicionar um caso que lide com ações do `addNodeText()`. Esse caso deve ser acionado sempre que há uma ação do tipo `ADD_NOTE` e ele deve retornar a propriedade `text` na `action` chegando como o novo `state`.
A ação é despachada no final do código. Quando terminar, rode o código e olhe o console. Isso é tudo que é necessário para enviar dados específicos de ações ao store e o usar quando você atualizar o `state` do store.
# --hints--
O criador de ação `addNoteText` deve retornar um objeto com chaves `type` e `text`.
```js
assert(
(function () {
const addNoteFn = addNoteText('__TEST__NOTE');
return addNoteFn.type === ADD_NOTE && addNoteFn.text === '__TEST__NOTE';
})()
);
```
Despachar uma ação do tipo `ADD_NOTE` com o criador de ação `addNodeText` deve atualizar o `state` para a string passada para o criador da ação.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(addNoteText('__TEST__NOTE'));
const newState = store.getState();
return initialState !== newState && newState === '__TEST__NOTE';
})()
);
```
# --seed--
## --seed-contents--
```js
const ADD_NOTE = 'ADD_NOTE';
const notesReducer = (state = 'Initial State', action) => {
switch(action.type) {
// Change code below this line
// Change code above this line
default:
return state;
}
};
const addNoteText = (note) => {
// Change code below this line
// Change code above this line
};
const store = Redux.createStore(notesReducer);
console.log(store.getState());
store.dispatch(addNoteText('Hello!'));
console.log(store.getState());
```
# --solutions--
```js
const ADD_NOTE = 'ADD_NOTE';
const notesReducer = (state = 'Initial State', action) => {
switch(action.type) {
// Change code below this line
case ADD_NOTE:
return action.text;
// Change code above this line
default:
return state;
}
};
const addNoteText = (note) => {
// Change code below this line
return {
type: ADD_NOTE,
text: note
}
// Change code above this line
};
const store = Redux.createStore(notesReducer);
console.log(store.getState());
store.dispatch(addNoteText('Hello Redux!'));
console.log(store.getState());
```

View File

@@ -0,0 +1,152 @@
---
id: 5a24c314108439a4d4036151
title: Usar um comando switch para lidar com várias ações
challengeType: 6
forumTopicId: 301449
dashedName: use-a-switch-statement-to-handle-multiple-actions
---
# --description--
Você pode dizer à store do Redux como lidar com vários tipos de ação. Digamos que você está gerenciando a autenticação de usuário na store do Redux. Você quer ter uma representação de estado para quando os usuários estiverem logados e quando forem desconectados. Você representa isso com um único objeto state com a propriedade `authenticated`. Você também precisa de criadores de ações que criem ações correspondentes ao login e logout do usuário, juntamente com os próprios objetos de ação.
# --instructions--
O editor de código tem uma store, actions e criadores de actions configurados para você. Preencha a função `reducer` para lidar com várias ações de autenticação. Use a instrução do JavaScript `switch` no `reducer` para responder a diferentes eventos de ação. Este é um padrão para escrever reducers do Redux. O comando switch deve mudar acima de `action.type` e retornar o estado de autenticação apropriado.
**Observação:** neste ponto, não se preocupe com imutabilidade do state, já que é pequeno e simples neste exemplo. Para cada ação, você pode retornar um novo objeto — por exemplo, `{authenticated: true}`. Além disso, não se esqueça de escrever um caso `default` na sua instrução switch que retorna o `state`atual. Isso é importante porque, como seu aplicativo tem vários redutores, eles são executados toda vez que uma ação é enviada, mesmo quando a ação não é relacionada a esse reducer. Nesses casos, você quer garantir que você retorna o `state` atual.
# --hints--
Chamar a função `loginUser` deve retornar um objeto com a propriedade `type` definida para a string `LOGIN`.
```js
assert(loginUser().type === 'LOGIN');
```
Chamar a função `logoutUser` deve retornar um objeto com a propriedade `type` definida para a string `LOGOUT`.
```js
assert(logoutUser().type === 'LOGOUT');
```
A store deve ser inicializada com um objeto com a propriedade `authenticated` definido como `false`.
```js
assert(store.getState().authenticated === false);
```
Despachar `loginUser` deve atualizar a propriedade `authenticated` no state da store para `true`.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(loginUser());
const afterLogin = store.getState();
return (
initialState.authenticated === false && afterLogin.authenticated === true
);
})()
);
```
Despachar `logoutUser` deve atualizar a propriedade `authenticated` no state da store para `false`.
```js
assert(
(function () {
store.dispatch(loginUser());
const loggedIn = store.getState();
store.dispatch(logoutUser());
const afterLogout = store.getState();
return (
loggedIn.authenticated === true && afterLogout.authenticated === false
);
})()
);
```
A função `authReducer` deve lidar com vários tipos de ação com um comando `switch`.
```js
(getUserInput) =>
assert(
getUserInput('index').toString().includes('switch') &&
getUserInput('index').toString().includes('case') &&
getUserInput('index').toString().includes('default')
);
```
# --seed--
## --seed-contents--
```js
const defaultState = {
authenticated: false
};
const authReducer = (state = defaultState, action) => {
// Change code below this line
// Change code above this line
};
const store = Redux.createStore(authReducer);
const loginUser = () => {
return {
type: 'LOGIN'
}
};
const logoutUser = () => {
return {
type: 'LOGOUT'
}
};
```
# --solutions--
```js
const defaultState = {
authenticated: false
};
const authReducer = (state = defaultState, action) => {
switch (action.type) {
case 'LOGIN':
return {
authenticated: true
}
case 'LOGOUT':
return {
authenticated: false
}
default:
return state;
}
};
const store = Redux.createStore(authReducer);
const loginUser = () => {
return {
type: 'LOGIN'
}
};
const logoutUser = () => {
return {
type: 'LOGOUT'
}
};
```

View File

@@ -0,0 +1,205 @@
---
id: 5a24c314108439a4d4036152
title: Usar const para action types
challengeType: 6
forumTopicId: 301450
dashedName: use-const-for-action-types
---
# --description--
Uma prática comum ao trabalhar com Redux é atribuir tipos de ação como constantes somente de leitura, fazendo referência a essas constantes onde quer que elas sejam usadas. Você pode refatorar o código com o qual você estava trabalhando para escrever os tipos de ação com declarações `const`.
# --instructions--
Declare `LOGIN` e `LOGOUT` como valores `const` e os atribua às strings `'LOGIN'` e `'LOGOUT'`, respectivamente. Então, edite o `authReducer()` e os criadores de ação para fazer referência a essas constantes ao invés de valores string.
**Observação:** é uma convenção escrever constantes com todas as letras maiúsculas, e essa prática também é padrão no Redux.
# --hints--
Chamar a função `loginUser` deve retornar um objeto com a propriedade `type` definida para a string `LOGIN`.
```js
assert(loginUser().type === 'LOGIN');
```
Chamar a função `logoutUser` deve retornar um objeto com a propriedade `type` definida para a string `LOGOUT`.
```js
assert(logoutUser().type === 'LOGOUT');
```
A store deve ser inicializada com um objeto com a propriedade `login` definida como `false`.
```js
assert(store.getState().authenticated === false);
```
Despachar `loginUser` deve atualizar a propriedade `login` no state da store para `true`.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(loginUser());
const afterLogin = store.getState();
return (
initialState.authenticated === false && afterLogin.authenticated === true
);
})()
);
```
Despachar `logoutUser` deve atualizar a propriedade `login` no state da store para `false`.
```js
assert(
(function () {
store.dispatch(loginUser());
const loggedIn = store.getState();
store.dispatch(logoutUser());
const afterLogout = store.getState();
return (
loggedIn.authenticated === true && afterLogout.authenticated === false
);
})()
);
```
A função `authReducer` deve lidar com vários tipos de ação com um comando `switch`.
```js
(getUserInput) =>
assert(
(function () {
return (
typeof authReducer === 'function' &&
getUserInput('index').toString().includes('switch') &&
getUserInput('index').toString().includes('case') &&
getUserInput('index').toString().includes('default')
);
})()
);
```
`LOGIN` e `LOGOUT` devem ser declarados como valores `const` e devem ser atribuídos às strings `LOGIN`e `LOGOUT`.
```js
const noWhiteSpace = __helpers.removeWhiteSpace(code);
assert(
(/constLOGIN=(['"`])LOGIN\1/.test(noWhiteSpace) &&
/constLOGOUT=(['"`])LOGOUT\1/.test(noWhiteSpace)) ||
/const(LOGIN|LOGOUT)=(['"`])\1\2,(?!\1)(LOGIN|LOGOUT)=(['"`])\3\4/.test(noWhiteSpace)
);
```
Os criadores de ação e o reducer devem fazer referência às constantes `LOGIN` e `LOGOUT`.
```js
(getUserInput) =>
assert(
(function () {
const noWhiteSpace = __helpers.removeWhiteSpace(
getUserInput('index').toString()
);
return (
noWhiteSpace.includes('caseLOGIN:') &&
noWhiteSpace.includes('caseLOGOUT:') &&
noWhiteSpace.includes('type:LOGIN') &&
noWhiteSpace.includes('type:LOGOUT')
);
})()
);
```
# --seed--
## --seed-contents--
```js
const defaultState = {
authenticated: false
};
const authReducer = (state = defaultState, action) => {
switch (action.type) {
case 'LOGIN':
return {
authenticated: true
}
case 'LOGOUT':
return {
authenticated: false
}
default:
return state;
}
};
const store = Redux.createStore(authReducer);
const loginUser = () => {
return {
type: 'LOGIN'
}
};
const logoutUser = () => {
return {
type: 'LOGOUT'
}
};
```
# --solutions--
```js
const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';
const defaultState = {
authenticated: false
};
const authReducer = (state = defaultState, action) => {
switch (action.type) {
case LOGIN:
return {
authenticated: true
}
case LOGOUT:
return {
authenticated: false
}
default:
return state;
}
};
const store = Redux.createStore(authReducer);
const loginUser = () => {
return {
type: LOGIN
}
};
const logoutUser = () => {
return {
type: LOGOUT
}
};
```

View File

@@ -0,0 +1,170 @@
---
id: 5a24c314108439a4d4036156
title: Usar o middleware para lidar com ações assíncronas
challengeType: 6
forumTopicId: 301451
dashedName: use-middleware-to-handle-asynchronous-actions
---
# --description--
Até agora, estes desafios têm evitado discutir ações assíncronas, mas elas constituem uma parte inevitável do desenvolvimento web. Em algum momento, você precisará chamar rotas assíncronas no seu aplicativo Redux, então como você lida com esses tipos de pedidos? Redux fornece o middleware desenhado especificamente para esse propósito, chamado midleware Redux Thunk. Aqui está uma breve descrição sobre como usar isso com o Redux.
Para incluir o middleware Redux Thunk, você o passa como um argumento para `Redux.applyMiddleware()`. Esta declaração é então fornecida como o segundo parâmetro opcional para a função `createStore()`. Dê uma olhada no código na parte inferior do editor para ver isso. Então, para criar uma ação assíncrona, você retorna uma função no criador de ação que recebe `dispatch` como argumento. Dentro dessa função, você pode despachar ações e executar requisições assíncronas.
Neste exemplo, uma requisição assíncrona é simulada com uma chamada a `setTimeout()`. É comum despachar uma ação antes de iniciar qualquer comportamento assíncrono para que o estado do seu aplicativo saiba que alguns dados estão sendo requisitados (este estado pode exibir um ícone de carregamento, por exemplo). Em seguida, ao receber os dados, você envia outra ação que carrega os dados como um payload junto com informações de que a ação foi concluída.
Lembre-se de que você está passando o `dispatch` como um parâmetro para este criador de ação especial. Isso é o que você vai usar para despachar suas ações, você simplesmente passa a ação diretamente para despachar e o middleware cuida do resto.
# --instructions--
Escreva os dois dispatches no criador de ação `handleAsync()`. Despache `requestingData()` antes do `setTimeout()` (a chamada de API simulada). Então, depois de receber os dados (simulados), despache a ação `receivedData()`, passando estes dados. Agora você sabe como lidar com ações assíncronas no Redux. Todo o resto continua a se comportar como antes.
# --hints--
O criador de ação `requestingData` deve retornar um objeto de tipo igual ao valor de `REQUESTING_DATA`.
```js
assert(requestingData().type === REQUESTING_DATA);
```
O criador de ação `receivedData` deve retornar um objeto de tipo igual ao valor de `REQUESTING_DATA`.
```js
assert(receivedData('data').type === RECEIVED_DATA);
```
`asyncDataReducer` deve ser uma função.
```js
assert(typeof asyncDataReducer === 'function');
```
Despachar o criador de ação `requestingData` deve atualizar a propriedade `state` da store de buscar para `true`.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(requestingData());
const reqState = store.getState();
return initialState.fetching === false && reqState.fetching === true;
})()
);
```
Despachar `handleAsync` deve enviar a ação de requisição de dados e, em seguida, enviar a ação de dados recebidos após um atraso.
```js
assert(
(function () {
const noWhiteSpace = __helpers.removeWhiteSpace(handleAsync.toString());
return (
noWhiteSpace.includes('dispatch(requestingData())') === true &&
noWhiteSpace.includes('dispatch(receivedData(data))') === true
);
})()
);
```
# --seed--
## --seed-contents--
```js
const REQUESTING_DATA = 'REQUESTING_DATA'
const RECEIVED_DATA = 'RECEIVED_DATA'
const requestingData = () => { return {type: REQUESTING_DATA} }
const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }
const handleAsync = () => {
return function(dispatch) {
// Dispatch request action here
setTimeout(function() {
let data = {
users: ['Jeff', 'William', 'Alice']
}
// Dispatch received data action here
}, 2500);
}
};
const defaultState = {
fetching: false,
users: []
};
const asyncDataReducer = (state = defaultState, action) => {
switch(action.type) {
case REQUESTING_DATA:
return {
fetching: true,
users: []
}
case RECEIVED_DATA:
return {
fetching: false,
users: action.users
}
default:
return state;
}
};
const store = Redux.createStore(
asyncDataReducer,
Redux.applyMiddleware(ReduxThunk.default)
);
```
# --solutions--
```js
const REQUESTING_DATA = 'REQUESTING_DATA'
const RECEIVED_DATA = 'RECEIVED_DATA'
const requestingData = () => { return {type: REQUESTING_DATA} }
const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }
const handleAsync = () => {
return function(dispatch) {
dispatch(requestingData());
setTimeout(function() {
let data = {
users: ['Jeff', 'William', 'Alice']
}
dispatch(receivedData(data));
}, 2500);
}
};
const defaultState = {
fetching: false,
users: []
};
const asyncDataReducer = (state = defaultState, action) => {
switch(action.type) {
case REQUESTING_DATA:
return {
fetching: true,
users: []
}
case RECEIVED_DATA:
return {
fetching: false,
users: action.users
}
default:
return state;
}
};
const store = Redux.createStore(
asyncDataReducer,
Redux.applyMiddleware(ReduxThunk.default)
);
```

View File

@@ -0,0 +1,114 @@
---
id: 5a24c314108439a4d4036159
title: Usar o operador spread em arrays
challengeType: 6
forumTopicId: 301452
dashedName: use-the-spread-operator-on-arrays
---
# --description--
Uma solução da ES6 para ajudar a impor imutabilidade do estado no Redux é o operador spread: `...`. O operador spread tem uma variedade de aplicações, uma das quais é adequada ao desafio anterior de produzir um novo array a partir de um array existente. Trata-se de uma sintaxe relativamente nova, mas comumente usada. Por exemplo, se você tem um array `myArray` e escreve:
```js
let newArray = [...myArray];
```
`newArray` agora é um clone de `myArray`. Os dois arrays ainda existem separadamente na memória. Se você executar uma mutação como `newArray.push(5)`, `myArray` não muda. O `...` efetivamente *espalha* os valores em `myArray` em um novo array. Para clonar um array, mas adicionar valores adicionais no novo array, você pode escrever `[...myArray, 'new value']`. Isso retornaria um novo array composto pelos valores em `myArray` e a string `new value` como o último valor. A sintaxe de spread pode ser usada várias vezes na composição de um array desta maneira, mas é importante notar que ele só faz uma cópia superficial do array. Ou seja, apenas proporciona operações de array imutáveis para arrays unidimensionais.
# --instructions--
Use o operador spread para retornar uma nova cópia do state quando uma tarefa (to-do) for adicionada.
# --hints--
A store do Redux deve existir e inicializar com um estado igual a `["Do not mutate state!"]`.
```js
assert(
(function () {
const initialState = store.getState();
return (
Array.isArray(initialState) === true &&
initialState[0] === 'Do not mutate state!'
);
})()
);
```
`addToDo` e `immutableReducer` ambos devem ser funções.
```js
assert(typeof addToDo === 'function' && typeof immutableReducer === 'function');
```
Despachar uma ação do tipo `ADD_TO_DO` no store Redux deve adicionar um item `todo` e NÃO deve modificar o state.
```js
assert(
(function () {
const initialState = store.getState();
const isFrozen = DeepFreeze(initialState);
store.dispatch(addToDo('__TEST__TO__DO__'));
const finalState = store.getState();
const expectedState = ['Do not mutate state!', '__TEST__TO__DO__'];
return isFrozen && DeepEqual(finalState, expectedState);
})()
);
```
O operador spread deve ser usado para retornar um novo estado.
```js
(getUserInput) => assert(getUserInput('index').includes('...state'));
```
# --seed--
## --seed-contents--
```js
const immutableReducer = (state = ['Do not mutate state!'], action) => {
switch(action.type) {
case 'ADD_TO_DO':
// Don't mutate state here or the tests will fail
return
default:
return state;
}
};
const addToDo = (todo) => {
return {
type: 'ADD_TO_DO',
todo
}
}
const store = Redux.createStore(immutableReducer);
```
# --solutions--
```js
const immutableReducer = (state = ['Do not mutate state!'], action) => {
switch(action.type) {
case 'ADD_TO_DO':
return [
...state,
action.todo
];
default:
return state;
}
};
const addToDo = (todo) => {
return {
type: 'ADD_TO_DO',
todo
}
}
const store = Redux.createStore(immutableReducer);
```

View File

@@ -0,0 +1,116 @@
---
id: 5a24c314108439a4d4036157
title: Escrever um contador com Redux
challengeType: 6
forumTopicId: 301453
dashedName: write-a-counter-with-redux
---
# --description--
Agora você aprendeu todos os princípios fundamentais do Redux! Você viu como criar criadores de ações e ações, criar uma store do Redux, despachar suas ações para a store e projetar atualizações de estado com reducers puros. Você até viu como gerenciar o estado complexo com a composição do reducer e a lidar com ações assíncronas. Estes exemplos são simplistas, mas estes conceitos são os princípios fundamentais do Redux. Se você os entendeu bem, você está pronto para começar a construir seu próprio aplicativo Redux. Os próximos desafios cobrem alguns detalhes sobre imutabilidade do `state`, mas primeiro, aqui está uma revisão de tudo o que você aprendeu até agora.
# --instructions--
Nesta lição, você implementará um contador simples com o Redux do zero. O básico é fornecido no editor de código, mas você terá que preencher os detalhes! Use os nomes que são fornecidos e defina os criadores de ação `incAction` e `decAction`, os tipos de ação `counterReducer()`, `INCREMENT` e `DECREMENT` e, finalmente, a `store` do Redux. Depois de ter terminado, você deve ser capaz de despachar as ações `INCREMENT` ou `DECREMENT` para incrementar ou decrementar o estado mantido na `store`. Boa sorte ao construir seu primeiro aplicativo Redux!
# --hints--
O criador de ação `incAction` deve retornar um objeto de ação com `type` igual ao valor de `INCREMENT`
```js
assert(incAction().type === INCREMENT);
```
O criador de ação `decAction` deve retornar um objeto de ação com `type` igual ao valor de `DECREMENT`
```js
assert(decAction().type === DECREMENT);
```
A store do Redux deve inicializar com o `state` igual a 0.
```js
assert(store.getState() === 0);
```
Despachar `incAction` na store do Redux deve incrementar o `state` por 1.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(incAction());
const incState = store.getState();
return initialState + 1 === incState;
})()
);
```
Despachar `decAction` na store do Redux deve decrementar o `state` por 1.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(decAction());
const decState = store.getState();
return initialState - 1 === decState;
})()
);
```
`counterReducer` deve ser uma função
```js
assert(typeof counterReducer === 'function');
```
# --seed--
## --seed-contents--
```js
const INCREMENT = null; // Define a constant for increment action types
const DECREMENT = null; // Define a constant for decrement action types
const counterReducer = null; // Define the counter reducer which will increment or decrement the state based on the action it receives
const incAction = null; // Define an action creator for incrementing
const decAction = null; // Define an action creator for decrementing
const store = null; // Define the Redux store here, passing in your reducers
```
# --solutions--
```js
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const counterReducer = (state = 0, action) => {
switch(action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
};
const incAction = () => {
return {
type: INCREMENT
}
};
const decAction = () => {
return {
type: DECREMENT
}
};
const store = Redux.createStore(counterReducer);
```