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: Combina múltiples reductores
challengeType: 6
forumTopicId: 301436
dashedName: combine-multiple-reducers
---
# --description--
Cuando el estado de tu aplicación empieza a ser más complejo, puede ser tentador dividir el estado en varias piezas. En su lugar, recuerda el primer principio de Redux: todo el estado de la aplicación se mantiene en un único objeto de estado en el almacén. Por lo tanto, Redux proporciona la composición de reductores como una solución para un modelo de estado complejo. Se definen varios reductores para manejar diferentes partes del estado de tu aplicación, y luego se componen estos reductores juntos en un reductor raíz. El reductor raíz se pasa al método Redux `createStore()`.
Para permitirnos combinar múltiples reductores juntos, Redux proporciona el método `combineReducers()`. Este método acepta un objeto como argumento en el que se definen las propiedades que asocian las claves a funciones reductoras específicas. El nombre que le des a las claves será utilizado por Redux como el nombre de la pieza de estado asociada.
Por lo general, es una buena práctica crear un reductor para cada pieza de estado de la aplicación cuando son distintas o únicas de alguna manera. Por ejemplo, en una aplicación para tomar notas con autenticación de usuario, un reductor podría encargarse de la autenticación, mientras que otro se encarga del texto y las notas que el usuario envía. Para tal aplicación, podríamos escribir el método `combineReducers()` así:
```js
const rootReducer = Redux.combineReducers({
auth: authenticationReducer,
notes: notesReducer
});
```
Ahora, la clave `notes` contendrá todo el estado asociado a nuestras notas y manejado por nuestro `notesReducer`. Así es como se pueden componer múltiples reductores para gestionar un estado de aplicación más complejo. En este ejemplo, el estado mantenido en el almacén Redux sería entonces un único objeto que contiene las propiedades `auth` y `notes`.
# --instructions--
Están las funciones `counterReducer()` y `authReducer()` proporcionadas en el editor de código, junto con un almacén Redux. Termina de escribir la función `rootReducer()` utilizando el método `Redux.combineReducers()`. Asigna `counterReducer` a una clave llamada `count` y `authReducer` a una clave llamada `auth`.
# --hints--
El `counterReducer` debe incrementar y disminuir el `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;
})()
);
```
El `authReducer` debe alternar el `state` de `authenticated` entre `true` y `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;
})()
);
```
El almacén `state` debe tener dos claves: `count`, que contiene un número, y `auth`, que contiene un objeto. El objeto `auth` debe tener una propiedad de `authenticated`, que contiene un booleano.
```js
assert(
(function () {
const state = store.getState();
return (
typeof state.auth === 'object' &&
typeof state.auth.authenticated === 'boolean' &&
typeof state.count === 'number'
);
})()
);
```
El `rootReducer` debe ser una función que combine el `counterReducer` y el `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: Copia un objeto con Object.assign
challengeType: 6
forumTopicId: 301437
dashedName: copy-an-object-with-object-assign
---
# --description--
Los últimos desafíos trabajaron con arreglos, pero hay maneras de ayudar a reforzar la inmutabilidad del estado cuando el estado es también un `object`. Una herramienta útil para el manejo de objetos es la utilidad `Object.assign()`. `Object.assign()` toma un objeto de destino y objetos de origen y asigna propiedades de los objetos de origen al objeto de destino. Las propiedades que coinciden se sobrescriben con las propiedades de los objetos de origen. Este comportamiento se utiliza comúnmente para hacer copias superficiales de objetos pasando un objeto vacío como primer argumento seguido por el/los objeto(s) que se desea(n) copiar. Aquí hay un ejemplo:
```js
const newObject = Object.assign({}, obj1, obj2);
```
Esto crea `newObject` como un nuevo `object`, que contiene las propiedades que existen actualmente en `obj1` y `obj2`.
# --instructions--
El estado y las acciones de Redux fueron modificados para manejar un `object` para el `state`. Edita el código para devolver un nuevo objeto `state` para las acciones de tipo `ONLINE`, que establece la propiedad `status` a la cadena `online`. Intenta utilizar `Object.assign()` para completar el desafío.
# --hints--
El almacén Redux debe existir e inicializarse con un estado equivalente al objeto `defaultState` declarado en la línea 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` deben ser funciones.
```js
assert(typeof wakeUp === 'function' && typeof immutableReducer === 'function');
```
El envío de una acción de tipo `ONLINE` debe actualizar la propiedad `status` del estado a `online` y NO debe mutar el 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` debe utilizarse para devolver el nuevo 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: Crea un almacén Redux
challengeType: 6
forumTopicId: 301439
dashedName: create-a-redux-store
---
# --description--
Redux es un framework de gestión de estados que se puede utilizar con un número de diferentes tecnologías web, incluyendo React.
En Redux, hay un único objeto de estado que es responsable de todo el estado de tu aplicación. Esto significa que si tuvieras una aplicación React con diez componentes, y cada componente tuviera su propio estado local, todo el estado de tu aplicación estaría definido por un único objeto de estado alojado en el `store` (almacén) de Redux. Este es el primer principio importante a entender cuando se aprende Redux: el almacén Redux es la única fuente de verdad cuando se trata del estado de la aplicación.
Esto también significa que cada vez que cualquier pieza de tu aplicación quiera actualizar el estado, **debe** hacerlo a través del almacén Redux. El flujo de datos unidireccional facilita el seguimiento de la gestión de estados en tu aplicación.
# --instructions--
El `store` de Redux es un objeto que guarda y gestiona el `state` de la aplicación. Hay un método llamado `createStore()` en el objeto Redux, que se utiliza para crear el `store` Redux. Este método toma una función `reducer` como argumento obligatorio. La función `reducer` se trata en un desafío posterior, y ya está definida para ti en el editor de código. Simplemente toma `state` como argumento y devuelve `state`.
Declara una variable `store` y asígnala al método `createStore()`, pasando el `reducer` como argumento.
**Nota:** El código del editor utiliza la sintaxis de los argumentos por defecto de ES6 para inicializar este estado y mantener un valor de `5`. Si no estás familiarizado con los argumentos por defecto, puedes consultar la sección [ES6 en el Plan de Estudios](https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/es6/set-default-parameters-for-your-functions) que cubre este tema.
# --hints--
El almacén Redux debe existir.
```js
assert(typeof store.getState === 'function');
```
El almacén Redux debe tener un valor de 5 para el 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: Define una acción Redux
challengeType: 6
forumTopicId: 301440
dashedName: define-a-redux-action
---
# --description--
Dado que Redux es un framework de gestión de estado, la actualización del estado es una de sus tareas principales. En Redux, todas las actualizaciones de estado se activan mediante acciones de envío. Una acción es simplemente un objeto de JavaScript que contiene información sobre un evento de acción que ha ocurrido. El almacén Redux recibe estos objetos de acción, y luego actualiza su estado de acuerdo a ello. A veces una acción Redux también lleva algunos datos. Por ejemplo, la acción lleva un nombre de usuario después de que un usuario inicia sesión. Aunque los datos son opcionales, las acciones deben llevar una propiedad `type` que especifica el "tipo" de acción que se ha producido.
Piensa en las acciones Redux como mensajeros que entregan información sobre los eventos que ocurren en tu aplicación al almacén Redux. A continuación, el almacén se encarga de actualizar el estado en función de la acción realizada.
# --instructions--
Escribir una acción Redux es tan sencillo como declarar un objeto con una propiedad de tipo. Declara un objeto `action` y dale una propiedad `type` establecida a la cadena `'LOGIN'`.
# --hints--
Debe existir un objeto `action`.
```js
assert(
(function () {
return typeof action === 'object';
})()
);
```
El objeto `action` debe tener una propiedad clave `type` con valor `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: Define un creador de acción
challengeType: 6
forumTopicId: 301441
dashedName: define-an-action-creator
---
# --description--
Después de crear una acción, el siguiente paso es enviar la acción al almacén Redux para que pueda actualizar su estado. En Redux, se definen creadores de acción para lograr esto. Un creador de acción es simplemente una función de JavaScript que devuelve una acción. En otras palabras, los creadores de acción crean objetos que representan eventos de acción.
# --instructions--
Define una función llamada `actionCreator()` que devuelve el objeto `action` cuando es llamado.
# --hints--
La función `actionCreator` debe existir.
```js
assert(typeof actionCreator === 'function');
```
La ejecución de la función `actionCreator` debe devolver el objeto `action`.
```js
assert(typeof action === 'object');
```
La `action` devuelta debe tener una propiedad clave `type` con 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: Envía un evento de acción
challengeType: 6
forumTopicId: 301442
dashedName: dispatch-an-action-event
---
# --description--
El método `dispatch` (enviar) es el que se utiliza para enviar acciones al almacén Redux. Llamar a `store.dispatch()` y pasar el valor devuelto por un creador de acción envía una acción de regreso al almacén.
Recordemos que los creadores de acción devuelven un objeto con una propiedad de tipo que especifica la acción que se ha producido. Luego, el método envía un objeto de acción al almacén Redux. Basándonos en el ejemplo del desafío anterior, las siguientes líneas son equivalentes, y ambas envían la acción de tipo `LOGIN`:
```js
store.dispatch(actionCreator());
store.dispatch({ type: 'LOGIN' });
```
# --instructions--
El almacén Redux en el editor de código tiene un estado inicializado que es un objeto que contiene una propiedad `login` actualmente establecida a `false`. También hay un creador de acción llamado `loginAction()` que devuelve una acción de tipo `LOGIN`. Envía la acción `LOGIN` al almacén Redux llamando al método `dispatch`, y pasa la acción creada por `loginAction()`.
# --hints--
La llamada a la función `loginAction` debe devolver un objeto con la propiedad `type` establecida a la cadena `LOGIN`.
```js
assert(loginAction().type === 'LOGIN');
```
El almacén debe ser inicializado con un objeto con la propiedad `login` establecida a `false`.
```js
assert(store.getState().login === false);
```
El método `store.dispatch()` debe utilizarse para enviar una acción de 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: Obtén el estado del almacén Redux
challengeType: 6
forumTopicId: 301443
dashedName: get-state-from-the-redux-store
---
# --description--
El objeto almacén Redux proporciona varios métodos que permiten interactuar con él. Por ejemplo, puedes recuperar el `state` actual que tiene el objeto almacén Redux con el método `getState()`.
# --instructions--
El código del desafío anterior se reescribe de forma más concisa en el editor de código. Utiliza `store.getState()` para recuperar el `state` del `store`, y asígnalo a una nueva variable `currentState`.
# --hints--
El almacén Redux debe tener un valor de 5 para el estado inicial.
```js
assert(store.getState() === 5);
```
Debe existir una variable `currentState` y se le debe asignar el estado actual del almacén 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: Maneja una acción en el almacén
challengeType: 6
forumTopicId: 301444
dashedName: handle-an-action-in-the-store
---
# --description--
Después de crear y enviar una acción, el almacén Redux necesita saber cómo responder a esa acción. Este es el trabajo de una función `reducer` (reductor). Los reductores en Redux son responsables de las modificaciones de estado que tienen lugar en respuesta a las acciones. Un `reducer` toma `state` y `action` como argumentos, y siempre devuelve un nuevo `state`. Es importante ver que esta es la **única** función del reductor. No tiene efectos secundarios: nunca llama a un endpoint del API y nunca tiene sorpresas ocultas. El reductor es simplemente una función pura que toma el estado y la acción, y luego devuelve el nuevo estado.
Otro principio clave en Redux es que `state` es de sólo lectura. En otras palabras, la función `reducer` debe **siempre** devolver una nueva copia de `state` y nunca modificar el estado directamente. Redux no impone la inmutabilidad del estado, sin embargo, tú eres responsable de imponerla en el código de tus funciones reductoras. Practicarás esto en desafíos posteriores.
# --instructions--
El editor de código tiene el ejemplo anterior, así como el inicio de una función `reducer` para ti. Rellena el cuerpo de la función `reducer` para que si recibe una acción de tipo `'LOGIN'` devuelva un objeto de estado con `login` establecido a `true`. De lo contrario, devuelve el `state` actual. Ten en cuenta que el `state` actual y la `action` enviada se pasan al reductor, por lo que puedes acceder al tipo de la acción directamente con `action.type`.
# --hints--
La llamada a la función `loginAction` debe devolver un objeto con la propiedad type establecida a la cadena `LOGIN`.
```js
assert(loginAction().type === 'LOGIN');
```
El almacén debe ser inicializado con un objeto con la propiedad `login` establecida a `false`.
```js
assert(store.getState().login === false);
```
El envío de `loginAction` debe actualizar la propiedad `login` en el estado del almacén a `true`.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(loginAction());
const afterState = store.getState();
return initialState.login === false && afterState.login === true;
})()
);
```
Si la acción no es de tipo `LOGIN`, el almacén debe devolver el estado actual.
```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: Nunca mutes el estado
challengeType: 6
forumTopicId: 301445
dashedName: never-mutate-state
---
# --description--
Estos desafíos finales describen varios métodos para hacer cumplir el principio clave de la inmutabilidad del estado en Redux. Estado inmutable significa que nunca se modifica el estado directamente, sino que se devuelve una nueva copia del estado.
Si tomaras una captura del estado de una aplicación Redux a lo largo del tiempo, verías algo como `state 1`, `state 2`, `state 3`,`state 4`, `...` y así sucesivamente donde cada estado puede ser similar al anterior, pero cada uno es un dato distinto. Esta inmutabilidad, de hecho, es lo que proporciona características tales como la depuración de viajes en el tiempo de la que puedes haber oído hablar.
Redux no impone activamente la inmutabilidad del estado en su almacén o reductores, esa responsabilidad recae en el programador. Afortunadamente, JavaScript (especialmente ES6) proporciona varias herramientas útiles que puedes utilizar para reforzar la inmutabilidad de tu estado, ya sea un `string`, `number`, `array`, u `object`. Ten en cuenta que las cadenas y los números son valores primitivos y son inmutables por naturaleza. En otras palabras, 3 siempre es 3. No se puede cambiar el valor del número 3. Sin embargo, un `array` u `object` es mutable. En la práctica, tu estado probablemente consistirá en un `array` u `object`, ya que son estructuras de datos útiles para representar muchos tipos de información.
# --instructions--
Hay un `store` y un `reducer` en el editor de código para gestionar los elementos de las tareas pendientes. Termina de escribir el caso `ADD_TO_DO` en el reductor para añadir una nueva tarea al estado. Hay algunas maneras de lograr esto con JavaScript estándar o ES6. Ve si puedes encontrar la forma de devolver un nuevo arreglo con el elemento de `action.todo` añadido al final.
# --hints--
El almacén Redux debe existir e inicializarse con un estado igual al arreglo `todos` del 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` deben ser funciones.
```js
assert(typeof addToDo === 'function' && typeof immutableReducer === 'function');
```
El envío de una acción de tipo `ADD_TO_DO` en el almacén Redux debe añadir un elemento `todo` y NO debe mutar el estado.
```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: Registra un escucha al almacén
challengeType: 6
forumTopicId: 301446
dashedName: register-a-store-listener
---
# --description--
Otro método al que tienes acceso en el objeto Redux `store` es `store.subscribe()`. Esto te permite suscribir funciones de escucha al almacén, que se llaman cada vez que se envía una acción contra el almacén. Un uso sencillo de este método es suscribir una función a tu almacén que simplemente registra un mensaje cada vez que se recibe una acción y se actualiza el almacén.
# --instructions--
Escribe una función callback que incremente la variable global `count` cada vez que el almacén recibe una acción, y pasa esta función al método `store.subscribe()`. Verás que `store.dispatch()` es llamado tres veces seguidas, cada vez pasando directamente un objeto de acción. Observa la salida de la consola entre los envíos de acción para ver cómo se producen las actualizaciones.
# --hints--
El envío de acción `ADD` en el almacén debe incrementar el estado en `1`.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch({ type: 'ADD' });
const newState = store.getState();
return newState === initialState + 1;
})()
);
```
Debe haber una función de escucha suscrita al almacén usando `store.subscribe`.
```js
(getUserInput) => assert(getUserInput('index').match(/store\s*\.\s*subscribe\(/gm));
```
El `store.subscribe` debe recibir una función.
```js
(getUserInput) => assert(getUserInput('index').match(/(\s*function\s*)|(\s*\(\s*\)\s*=>)/gm))
```
El callback a `store.subscribe` también debe incrementar la variable global `count` a medida que se actualiza el almacén.
```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: Elimina un elemento de un arreglo
challengeType: 6
forumTopicId: 301447
dashedName: remove-an-item-from-an-array
---
# --description--
Es hora de practicar la eliminación de elementos de un arreglo. Aquí también se puede utilizar el operador de propagación. Otros métodos útiles de JavaScript son `slice()` y `concat()`.
# --instructions--
El reductor y el creador de acción fueron modificados para eliminar un elemento de un arreglo en función del índice del elemento. Termina de escribir el reductor para que devuelva un nuevo arreglo de estados con el elemento en el índice específico eliminado.
# --hints--
El almacén Redux debe existir e inicializarse con un 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` deben ser funciones.
```js
assert(
typeof removeItem === 'function' && typeof immutableReducer === 'function'
);
```
Enviar el creador de acción `removeItem` debe eliminar elementos del estado y NO debe mutar el estado.
```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: Envía datos de acción al almacén
challengeType: 6
forumTopicId: 301448
dashedName: send-action-data-to-the-store
---
# --description--
A estas alturas ya has aprendido a enviar acciones al almacén de Redux, pero hasta ahora estas acciones no contenían más información que un `type`. También puedes enviar datos específicos junto con sus acciones. De hecho, esto es muy común porque las acciones suelen originarse a partir de alguna interacción del usuario y suelen llevar consigo algunos datos. El almacén Redux a menudo necesita conocer estos datos.
# --instructions--
Hay un `notesReducer()` básico y un creador de acción `addNoteText()` definido en el editor de código. Termina el cuerpo de la función `addNoteText()` para que devuelva un objeto `action`. El objeto debe incluir una propiedad `type` con un valor de `ADD_NOTE`, y también una propiedad `text` establecida a los datos de `note` que se pasa al creador de acción. Cuando llames al creador de acción, pasarás información específica de la nota a la que puedes acceder para el objeto.
A continuación, termina de escribir la sentencia `switch` en el `notesReducer()`. Necesitas añadir un caso que maneje las acciones `addNoteText()`. Este caso debe activarse siempre que haya una acción de tipo `ADD_NOTE` y debe devolver la propiedad `text` de la `action` entrante como el nuevo `state`.
La acción es enviada en la parte inferior del código. Una vez que hayas terminado, ejecuta el código y observa la consola. Eso es todo lo que se necesita para enviar datos específicos de la acción al almacén y utilizarlos cuando se actualiza el `state` del almacén.
# --hints--
El creador de acción `addNoteText` debe devolver un objeto con las claves `type` y `text`.
```js
assert(
(function () {
const addNoteFn = addNoteText('__TEST__NOTE');
return addNoteFn.type === ADD_NOTE && addNoteFn.text === '__TEST__NOTE';
})()
);
```
Enviar una acción de tipo `ADD_NOTE` con el creador de acción `addNoteText` debe actualizar el `state` a la cadena pasada al creador de acción.
```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: Usa una sentencia Switch para manejar múltiples acciones
challengeType: 6
forumTopicId: 301449
dashedName: use-a-switch-statement-to-handle-multiple-actions
---
# --description--
Puedes decirle al almacén Redux cómo manejar múltiples tipos de acción. Digamos que estás gestionando la autenticación de usuarios en tu almacén Redux. Quieres tener una representación de estado para cuando los usuarios están conectados y cuando están desconectados. Esto se representa con un único objeto de estado con la propiedad `authenticated`. También se necesitan creadores de acción que creen acciones correspondientes al inicio y cierre de sesión de los usuarios, junto con los propios objetos de acción.
# --instructions--
El editor de código tiene un almacén, acciones y creadores de acción configurados para ti. Rellena la función `reducer` para manejar múltiples acciones de autenticación. Usa una sentencia `switch` de JavaScript en el `reducer` para responder a diferentes eventos de acción. Este es un patrón estándar en la escritura de reductores Redux. La sentencia switch debe cambiar sobre `action.type` y devolver el estado de autenticación apropiado.
**Nota:** En este punto, no te preocupes por la inmutabilidad del estado, ya que es pequeña y simple en este ejemplo. Para cada acción, puedes devolver un nuevo objeto, por ejemplo, `{authenticated: true}`. Además, no olvides escribir un caso `default` en tu sentencia switch que devuelva el `state` actual. Esto es importante porque una vez que tu aplicación tiene múltiples reductores, todos ellos se ejecutan cada vez que se realiza un envío de acción, incluso cuando la acción no está relacionada con ese reductor. En tal caso, querrás asegurarte de que devuelves el `state` actual.
# --hints--
La llamada a la función `loginUser` debe devolver un objeto con la propiedad type establecida a la cadena `LOGIN`.
```js
assert(loginUser().type === 'LOGIN');
```
La llamada a la función `logoutUser` debe devolver un objeto con la propiedad type establecida a la cadena `LOGOUT`.
```js
assert(logoutUser().type === 'LOGOUT');
```
El almacén debe ser inicializado con un objeto con una propiedad `authenticated` establecida a `false`.
```js
assert(store.getState().authenticated === false);
```
El envío de `loginUser` debe actualizar la propiedad `authenticated` en el estado del almacén a `true`.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(loginUser());
const afterLogin = store.getState();
return (
initialState.authenticated === false && afterLogin.authenticated === true
);
})()
);
```
El envío de `logoutUser` debe actualizar la propiedad `authenticated` en el estado del almacén a `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
);
})()
);
```
La función `authReducer` debe manejar múltiples tipos de acción con una sentencia `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: Usa const para los tipos de acción
challengeType: 6
forumTopicId: 301450
dashedName: use-const-for-action-types
---
# --description--
Una práctica común cuando se trabaja con Redux es asignar tipos de acción como constantes de sólo lectura, y luego hacer referencia a estas constantes dondequiera que se utilicen. Puedes refactorizar el código con el que estás trabajando para escribir los tipos de acción como declaraciones `const`.
# --instructions--
Declara `LOGIN` y `LOGOUT` como valores `const` y asígnalos a las cadenas `'LOGIN'` y `'LOGOUT'`, respectivamente. Luego, edita el `authReducer()` y los creadores de acción para que hagan referencia a estas constantes en lugar de valores de cadena.
**Nota:** Generalmente es una convención escribir las constantes en mayúsculas, y esto es una práctica estándar en Redux también.
# --hints--
La llamada a la función `loginUser` debe devolver un objeto con la propiedad `type` establecida a la cadena `LOGIN`.
```js
assert(loginUser().type === 'LOGIN');
```
La llamada a la función `logoutUser` debe devolver un objeto con la propiedad `type` establecida a la cadena `LOGOUT`.
```js
assert(logoutUser().type === 'LOGOUT');
```
El almacén debe ser inicializado con un objeto con la propiedad `login` establecida a `false`.
```js
assert(store.getState().authenticated === false);
```
El envío de `loginUser` debe actualizar la propiedad `login` en el estado del almacén a `true`.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(loginUser());
const afterLogin = store.getState();
return (
initialState.authenticated === false && afterLogin.authenticated === true
);
})()
);
```
El envío de `logoutUser` debe actualizar la propiedad `login` en el estado del almacén a `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
);
})()
);
```
La función `authReducer` debe manejar múltiples tipos de acción con una sentencia 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` y `LOGOUT` deben declararse como valores `const` y se les debe asignar cadenas de `LOGIN` y `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)
);
```
Los creadores de la acción y el reductor deben hacer referencia a las constantes `LOGIN` y `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: Usa middleware para manejar acciones asíncronas
challengeType: 6
forumTopicId: 301451
dashedName: use-middleware-to-handle-asynchronous-actions
---
# --description--
Hasta ahora hemos evitado hablar de las acciones asíncronas, pero son una parte inevitable del desarrollo web. En algún momento necesitarás llamar a endpoints asíncronos en tu aplicación Redux, así que ¿cómo manejas este tipo de peticiones? Redux proporciona un middleware diseñado específicamente para este propósito, llamado Redux Thunk middleware. Aquí hay una breve descripción de cómo usar esto con Redux.
Para incluir el Redux Thunk middleware, lo pasas como argumento a `Redux.applyMiddleware()`. Esta declaración se proporciona entonces como un segundo parámetro opcional a la función `createStore()`. Echa un vistazo al código en la parte inferior del editor para ver esto. Entonces, para crear una acción asíncrona, se devuelve una función en el creador de acción que toma `dispatch` como argumento. Dentro de esta función, se pueden enviar acciones y realizar peticiones asíncronas.
En este ejemplo, se simula una petición asíncrona con una llamada `setTimeout()`. Es común enviar una acción antes de iniciar cualquier comportamiento asíncrono para que el estado de tu aplicación sepa que se están solicitando algunos datos (este estado podría mostrar un icono de carga, por ejemplo). Luego, una vez que recibes los datos, envía otra acción que lleva los datos como carga útil junto con la información de que la acción se ha completado.
Recuerda que estás pasando `dispatch` como parámetro a este creador de acciones especiales. Esto es lo que usarás para enviar tus acciones, simplemente pasas la acción directamente a dispatch y el middleware se encarga del resto.
# --instructions--
Escribe ambos envíos en el creador de acción `handleAsync()`. Envía `requestingData()` antes del `setTimeout()` (la llamada a la API simulada). A continuación, después de recibir los datos (fingidos), envía la acción `receivedData()`, pasando estos datos. Ahora ya sabes cómo manejar las acciones asíncronas en Redux. Todo lo demás sigue comportándose como antes.
# --hints--
El creador de acción `requestingData` debe devolver un objeto de tipo igual al valor de `REQUESTING_DATA`.
```js
assert(requestingData().type === REQUESTING_DATA);
```
El creador de acción `receivedData` debe devolver un objeto de tipo igual al valor de `RECEIVED_DATA`.
```js
assert(receivedData('data').type === RECEIVED_DATA);
```
`asyncDataReducer` debe ser una función.
```js
assert(typeof asyncDataReducer === 'function');
```
Enviar el creador de acción `requestingData` debe actualizar la propiedad store `state` de fetching a `true`.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(requestingData());
const reqState = store.getState();
return initialState.fetching === false && reqState.fetching === true;
})()
);
```
El envío de `handleAsync` debe enviar la acción de solicitud de datos y luego enviar la acción de datos recibidos después de un retraso.
```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: Usa el operador de propagación en arreglos
challengeType: 6
forumTopicId: 301452
dashedName: use-the-spread-operator-on-arrays
---
# --description--
Una solución de ES6 para ayudar a reforzar la inmutabilidad del estado en Redux es el operador de propagación: `...`. El operador de propagación tiene una variedad de aplicaciones, una de las cuales es muy adecuada para el desafío anterior de producir un nuevo arreglo a partir de un arreglo existente. Se trata de una sintaxis relativamente nueva, pero de uso común. Por ejemplo, si tienes un arreglo `myArray` y escribes:
```js
let newArray = [...myArray];
```
`newArray` es ahora un clon de `myArray`. Ambos arreglos siguen existiendo por separado en la memoria. Si realizas una mutación como `newArray.push(5)`, `myArray` no cambia. El `...` efectivamente *distribuye* los valores de `myArray` en un nuevo arreglo. Para clonar un arreglo pero añadir valores adicionales en el nuevo arreglo, se podría escribir `[...myArray, 'new value']`. Esto devolvería un nuevo arreglo compuesto por los valores de `myArray` y la cadena `new value` como último valor. La sintaxis de propagación se puede utilizar varias veces en la composición de arreglos como este, pero es importante tener en cuenta que sólo hace una copia superficial del arreglo. Es decir, sólo proporciona operaciones de arreglos inmutables para arreglos unidimensionales.
# --instructions--
Utiliza el operador de propagación para devolver una nueva copia del estado cuando se añade una tarea.
# --hints--
El almacén Redux debe existir e inicializarse con un 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` deben ser funciones.
```js
assert(typeof addToDo === 'function' && typeof immutableReducer === 'function');
```
Enviar una acción de tipo `ADD_TO_DO` en el almacén Redux debe añadir un elemento `todo` y NO debe mutar el estado.
```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);
})()
);
```
El operador de propagación debe utilizarse para devolver el nuevo 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: Escribe un contador con Redux
challengeType: 6
forumTopicId: 301453
dashedName: write-a-counter-with-redux
---
# --description--
¡Ahora ya has aprendido todos los principios básicos de Redux! Has visto cómo crear acciones y creadores de acción, crear un almacén Redux, enviar tus acciones contra el almacén y diseñar actualizaciones de estado con reductores puros. Incluso has visto cómo gestionar estados complejos con la composición de reductores y manejar acciones asíncronas. Estos ejemplos son simplistas, pero estos conceptos son los principios básicos de Redux. Si los entiendes bien, estás listo para empezar a construir tu propia aplicación Redux. Los próximos desafíos cubren algunos de los detalles relacionados con la inmutabilidad de `state`, pero primero, aquí hay un repaso de todo lo que has aprendido hasta ahora.
# --instructions--
En esta lección, implementarás un simple contador con Redux desde cero. El editor de código proporciona lo básico, ¡pero tendrás que completar los detalles! Utiliza los nombres que se proporcionan y define los creadores de acciones `incAction` y `decAction`, el `counterReducer()`, los tipos de acción `INCREMENT` y `DECREMENT`, y finalmente el `store` de Redux. Una vez que hayas terminado deberías poder enviar acciones `INCREMENT` o `DECREMENT` para incrementar o disminuir el estado mantenido en el `store`. ¡Buena suerte construyendo tu primera aplicación Redux!
# --hints--
El creador de acción `incAction` debe devolver un objeto de acción con `type` igual al valor de `INCREMENT`
```js
assert(incAction().type === INCREMENT);
```
El creador de acción `decAction` debe devolver un objeto de acción con `type` igual al valor de `DECREMENT`
```js
assert(decAction().type === DECREMENT);
```
El almacén Redux debe inicializarse con un `state` de 0.
```js
assert(store.getState() === 0);
```
El envío de `incAction` en el almacén Redux debe incrementar el `state` en 1.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(incAction());
const incState = store.getState();
return initialState + 1 === incState;
})()
);
```
El envío de `decAction` en el almacén Redux debe disminuir el `state` en 1.
```js
assert(
(function () {
const initialState = store.getState();
store.dispatch(decAction());
const decState = store.getState();
return initialState - 1 === decState;
})()
);
```
`counterReducer` debe ser una función
```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);
```