diff --git a/src/content/lesson/context-api.es.md b/src/content/lesson/context-api.es.md index 6c1519f12..129f125d4 100644 --- a/src/content/lesson/context-api.es.md +++ b/src/content/lesson/context-api.es.md @@ -155,6 +155,6 @@ return
## Prueba el código en vivo - + -
Haz clic aquí para abrir el demo en una nueva ventana
+
Haz clic aquí para abrir el demo en una nueva ventana
diff --git a/src/content/lesson/context-api.md b/src/content/lesson/context-api.md index 491fd0863..9720a760d 100644 --- a/src/content/lesson/context-api.md +++ b/src/content/lesson/context-api.md @@ -159,6 +159,6 @@ return
## Test the code live - + -
Click here to open demo in a new window
+
Click here to open demo in a new window
diff --git a/src/content/lesson/managing-react-app-data.es.md b/src/content/lesson/managing-react-app-data.es.md deleted file mode 100644 index f2b396564..000000000 --- a/src/content/lesson/managing-react-app-data.es.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -title: "¿Cómo manejar los datos de tu aplicación react?" -subtitle: "Sin Flux, React es solo una linda librería de front-end. React Flux lo convertirá en un marco de trabajo, que le dará a tu aplicación una estructura definida, ocupándose de la capa de procesamiento de datos y más cosas de flux." -cover_local: "../../assets/images/b84e07e5-5761-469b-85bb-f7afc87d4dc9.png" -textColor: "white" -date: "2020-10-19T16:36:31+00:00" -tags: ["reactjs","javascript"] -status: "published" - ---- - -En el eterno de debate de como manejar un estado centralizado han habido muchos planteamientos y en la misma medida que las necesidades de las aplicaciones evolucionan, también lo hacen las estrategias para resolver este problema. - -Una solución recomendada y que escala muy bien es el uso combinado de los hooks `useContext` y `useReducer`. Con el contexto puedes exponer un estado y funciones para todos los componentes que se encuentren dentro, esto va a prevenir que caigas en el abismo del 'prop drilling' y solo hacer llamado al contexto desde el componente que lo necesita. [Aquí puedes ver](https://4geeks.com/es/lesson/context-api-es) mas en detalle como funciona este hook. - -La otra pieza del rompecabezas es el reducer, este permite encapsular lógica y estado, de manera que se puede reutilizar y compartir entre varios componentes. El reducer permite administrar estados mas complejos y también hace posible escribir pruebas unitarias para las acciones. - -## Vamos a implementar una lista de tareas - -### 1) Crearemos un reducer para manejar el estado y las acciones - -Para poder tomar el control del flujo de los datos en nuestra aplicación utilizaremos un `reducer` para agrupar las funciones y la lógica de la aplicación (actions) junto con los datos que manejan y que tienen que estar disponible para los componentes (state). - -Por ahora solo diremos que el reducer es una función que genera un estado nuevo cada vez que se ejecuta y lo que haga dependerá de la información que reciba en la función `action`. Esto nos permitirá llamar a las `actions` para actualizar el estado como lo indica el patrón flux. Para entender en detalle como funciona un reducer, puedes [leer esté artículo](https://4geeks.com/es/lesson/que-es-usereducer-react) donde lo explicamos a profundidad. - -```javascript -// Esta es la función reducer -const TaskReducer = (state, action) => { - // Dependiendo del type de la acción realiza una tarea distinta - switch (action.type) { - case "add": - return [...state, action.payload]; - case "remove": - let newState=[...state] - newState.splice(action.index, 1); - return newState - default: - return state; - } -}; -``` - -El siguiente paso es hacer que esta función esté disponible para todos los componentes de mi aplicación, para eso utilizaremos un contexto con el hook `useReducer`, el cual nos va a permitir crear el estado y la función `actions` para ponerla a disposición del resto de la aplicación. - -```jsx -//TaskContext.jsx -import { useReducer, createContext } from "react"; - -// Creamos el contexto vacío -const TaskContext = createContext(null); - -const TaskReducer = (state, action) => { - // Aquí va el reducer que se definió anteriormente👆 -}; - -// Crearemos un componente que va a envolver nuestra aplicación en el contexto -export function TaskProvider({ children }) { - // Creamos el state 'tasks' y el despachador 'taskActions' - // adicionalmente pasamos como estado inicial un arreglo vacío - const [tasks, taskActions ]= useReducer(TaskReducer, []); - return ( - {/* Creamos el contexto con nuestro state y actions */} - {children} - ); -} - -// Es necesario exportar el contexto para usarlo en otros componentes -export default TaskContext; -``` - -Ya con esto tenemos listo nuestro contexto con las tasks, ahora solo falta envolver nuestra aplicación en este componente para empezar a utilizarlo. - -```jsx -//index.jsx -import React from "react"; -import ReactDOM from "react-dom/client"; -import App from "./App"; -import { TaskProvider } from "./TaskContext.jsx"; - -ReactDOM.createRoot(document.getElementById("root")).render( - - - - - , -); - -``` -Ahora solo queda llamar al contexto desde los componentes que necesiten hacer uso de nuestras tareas. - -### 2) Empecemos por agregar una nueva tarea - -Para ello usaremos un componente que muestre una caja de texto y un botón que realiza la acción de agregar la tarea, todo dentro de un formulario para facilitar el manejo del evento de envío(submit). - -Todo esto es básico de un formulario en react, pero como queremos utilizar las `actions` del contexto, necesitamos llamarlo en el componente. - -```jsx -import { tasks, useContext } from "react"; -import TaskContext from "./TaskContext.jsx"; - -export default function AddItem() { - const { taskActions } = useContext(TaskContext); - // A partir de este punto tenemos disponibles las actions del reducer - // ... -} -``` - -Para hacer uso de estas `actions` se llama a la función y se le pasa como parámetro un objeto con la propiedades de la acción que queremos realizar, siendo la mas importante `type` que indica la acción especifica a ejecutar. El resto de las propiedades son datos opcionales que pueden ser requeridos por la acción. - -```jsx -// AddItem.jsx -import { useContext } from "react"; -import TaskContext from "./TaskContext.jsx"; - -export default function AddItem() { - // Hacemos uso del contexto y accedemos a la función 'taskActions' - const { taskActions } = useContext(TaskContext); - function handleAddTask(e) { - e.preventDefault(); - // Llamamos al actions especificándole 'type' - // asi como también la tarea que se va a agregar - let textbox = e.target.elements.task; - taskActions({ type: "add", payload: textbox.value }); - textbox.value = ""; - } - return ( -
  • -
    - - -
    -
  • - ); -} -``` - -### 3) Ahora vamos a mostrar la lista - -De la misma forma como accedemos a `taskActions`, también podemos acceder al objeto `tasks` que contiene el estado con la lista. Igual que antes, debemos hacer uso de `useContext` en nuestro componente. - -```jsx -import { useContext } from "react"; -import "./App.css"; -import TaskContext from "./TaskContext.jsx"; -import ListItem from "./ListItem.jsx"; -import AddItem from "./AddItem.jsx"; - -export default function App() { - // Accedemos al contexto, pero esta vez solo vamos a usar 'tasks' - const {tasks} = useContext(TaskContext); - - return ( -
    -

    Todo list

    -
      - - {tasks.map((task, index) => ( - - ))} -
    -
    - ); -} -``` - -Puedes notar que aparece el componente `AddItem` que vimos previamente y desde donde se pueden agregar tarea. Luego de eso se hace el renderizado de la lista con la función `map`, pero notamos que se esta usando un componente `ListItem` para mostrar los elementos, no solo eso sino que ahi también corresponde hacer la eliminación de la tarea, veamos ese componente. - -### 4) Eliminación de items - -Si bien el renderizado es básico (un elemento `li` con el texto y un botón), lo interesante es como hacemos la eliminación del item con las actions. - -***Todo comienza cuando el usuario haga clic en el icono de la papelera. Es por eso que necesitamos iniciar nuestra aplicación escuchando el típico evento onClick en el botón de eliminar.*** - -```jsx - onClick={() => taskActions({ type: "remove", index })} -``` - -Notamos que el llamado al action es parecido al que usamos para agregar items, pero se le esta pasando un parámetro distinto llamado `index`, que le indica al dispatcher que elemento va a eliminar. Asi como vimos en ambos ejemplos, podemos pasar la data que necesite nuestra action al momento de llamarla como parámetros adicionales. - -```jsx -import { useContext } from "react"; -import TaskContext from "./TaskContext.jsx"; - -export default function ListItem({ task, index }) { - const { taskActions } = useContext(TaskContext); - - return ( -
  • - {task} - -
  • - ); -} -```` - -## Resultado final - -Ya hemos implementado la lógica de nuestra aplicación en un contexto aplicando el patrón flux, permitiendo su uso en distintos componentes. A continuación podemos ver el resultado final. - - - -Puedes ver mas tutoriales relacionados con react en [ésta categoría.](https://4geeks.com/interactive-exercise/react-js-tutorial-exercises) \ No newline at end of file diff --git a/src/content/lesson/managing-react-app-data.md b/src/content/lesson/managing-react-app-data.md deleted file mode 100644 index 142c5df13..000000000 --- a/src/content/lesson/managing-react-app-data.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -title: "Learn What is React Flux" -subtitle: "Without Flux, React is just a cute front-end library. React Flux will make it a framework, giving your application a defined structure, taking care of the data-processing layer, and more about flux." -cover_local: "../../assets/images/b84e07e5-5761-469b-85bb-f7afc87d4dc9.png" -textColor: "white" -date: "2020-10-19T16:36:31+00:00" -tags: ["reactjs","flux"] -status: "published" - ---- - -In the endless debate about how to manage a centralized state there has been many proposals and in the same manner that the needs of the apps evolve, also evolve the strategies to solve this problem. - -One recommended solution that also scales very well is the combined use of the hooks `useContext` y `useReducer`. With the context you can expose a state and functions for all the components within it, this will prevent a downfall to the abyss of 'prop drilling' and just call the context on the components where is necessary. [Here you can see](https://4geeks.com/lesson/context-api) in more detail how this hook works. - -The other piece of the puzzle is the recuder, witch allows to encapsulate state and logic, in a way that can be reused and share by other components. The reducer allows to manage more complex states and also lets you write unit tests for the actions. - -## Building a to-do list with flux - -### 1) Let's build a reducer to manage the state and actions - -To take control over the flow of the data in our application we'll use a `reducer` to group the functions and the logic of the application (actions) along with the data that they handle and must be available to all the other components (state). - -For now, let's just say that the reducer is a function that generates a new state every time it runs, and what it does depends on the information passed to the `action` function. This allows us to call the `actions` to update the state as indicates the flux pattern. To understand better how a reducer works, you can read [this article](https://4geeks.com/lesson/optimize-react-components-usereducer) where we cover this in more depth. - -```javascript -// This is the reducer function -const TaskReducer = (state, action) => { - // Depending on the action type, it performs a specific action - switch (action.type) { - case "add": - return [...state, action.payload]; - case "remove": - let newState=[...state] - newState.splice(action.index, 1); - return newState - default: - return state; - } -}; -``` - -The next step is to make this function available for all the other components of the application, for that we'll use a context with the hook `useReducer`, which will allow us to create a state and the `actions` function to publish it to the rest of the components. - -```jsx -//TaskContext.jsx -import { useReducer, createContext } from "react"; - -// Create an empty context -const TaskContext = createContext(null); - -const TaskReducer = (state, action) => { - // Here is the reducer defined previously👆 -}; - -// Create a component to wrap our application within the context -export function TaskProvider({ children }) { - // Create the state 'tasks' and the dispatcher 'taskActions' - // additionally we'll pass an empty array as an initial value. - const [tasks, taskActions ]= useReducer(TaskReducer, []); - return ( - {/* Create the context with our state and actions */} - {children} - ); -} - -// Is necessary to export the context for it to be used in other components. -export default TaskContext; -``` - -Now the context is ready with our tasks, all we have to do is wrap our app with this component to start using our context. - -```jsx -//index.jsx -import React from "react"; -import ReactDOM from "react-dom/client"; -import App from "./App"; -import { TaskProvider } from "./TaskContext.jsx"; - -ReactDOM.createRoot(document.getElementById("root")).render( - - - - - , -); - -``` -The only thing left to do is to call the context in the components that need to use our tasks. - -### 2) Let's start adding a new task - -For that, we'll use a component that shows a textbox and a button that dispatches the adding action, all inside a form to ease the handle of the submit event. - -All that is basic stuff, but since we want to use the context `actions`, we need to call it from within the component. - -```jsx -import { tasks, useContext } from "react"; -import TaskContext from "./TaskContext.jsx"; - -export default function AddItem() { - const { taskActions } = useContext(TaskContext); - // From this point onwards we have the reducer's actions available. - // ... -} -``` - -To make use of these `actions` we call the function passing as a parameter an object with the properties of the action we want to call, being the most important the property `type` that indicates the specific action to execute. The rest of the properties are optional data that may be required by the action itself. - -```jsx -// AddItem.jsx -import { useContext } from "react"; -import TaskContext from "./TaskContext.jsx"; - -export default function AddItem() { - // Using the context we access the 'taskActions' function. - const { taskActions } = useContext(TaskContext); - function handleAddTask(e) { - e.preventDefault(); - // Calling the action specifying the 'type' - // as well as task data that will be added. - let textbox = e.target.elements.task; - taskActions({ type: "add", payload: textbox.value }); - textbox.value = ""; - } - return ( -
  • -
    - - -
    -
  • - ); -} -``` - -### 3) Now let's show the list - -In the same manner that we access `taskActions`, we can also access the `tasks` object that contains the state of the list. Just like before, we must use the `useContext` hook in our component. - -```jsx -import { useContext } from "react"; -import "./App.css"; -import TaskContext from "./TaskContext.jsx"; -import ListItem from "./ListItem.jsx"; -import AddItem from "./AddItem.jsx"; - -export default function App() { - // Accessing the context, but this time only using 'tasks' - const {tasks} = useContext(TaskContext); - - return ( -
    -

    Todo list

    -
      - - {tasks.map((task, index) => ( - - ))} -
    -
    - ); -} -``` - -You can see that we are using the `AddItem` component where you can add a task. After that is being rendered the list with a `map` function, but a component `ListItem` is being used to show this element, and it also has the functionality to remove a task, let's check out that component. - -### 4) Task deletion - -Even though the render is very basic (a `li` element with the text and a button), what's interesting is how we delete an item with the actions. - -***I all stats with the user clicking the trash can icon. That's why we need to start our component by listening to the classic onClick event on the delete button,*** - -```jsx - onClick={() => taskActions({ type: "remove", index })} -``` - -We notice that the call to `actions` is similar to the one used to add items, but in this case is receiving a different parameter called `index`, which indicates to the dispatcher which elements are going to be deleted. Just like we saw in both examples, we can pass any data that's needed to the action at the moment of calling it, as additional parameters. - -```jsx -import { useContext } from "react"; -import TaskContext from "./TaskContext.jsx"; - -export default function ListItem({ task, index }) { - const { taskActions } = useContext(TaskContext); - - return ( -
  • - {task} - -
  • - ); -} -```` - -## Final result - -We have implemented the logic of our application in a context applying the flux pattern, allowing its use in different components. Now we can see the final result working. - - - -Puedes ver mas tutoriales relacionados con react en [ésta categoría.](https://4geeks.com/interactive-exercise/react-js-tutorial-exercises) \ No newline at end of file diff --git a/src/content/lesson/what-is-react-flux.es.md b/src/content/lesson/what-is-react-flux.es.md index 4b0e1a2f5..43c80d8bf 100644 --- a/src/content/lesson/what-is-react-flux.es.md +++ b/src/content/lesson/what-is-react-flux.es.md @@ -9,9 +9,6 @@ status: "published" --- -> ### ⚠️Advertencia⚠️ -> El siguiente artículo tiene información que actualmente está desaprobada por la comunidad de React. En su lugar se recomienda utilizar los hooks `useContext` y `useReducer` en conjunto para manejar la información de una aplicación de forma nativa, aprende a hacerlo en [este artículo](https://4geeks.com/es/lesson/managing-react-app-data-es). La información presentada en este artículo aún puede ser útil para sistemas legados que estén en funcionamiento con esta librería, aunque no se recomiendo su uso para nuevos desarrollos. - ¿Recuerdas que siempre decimos que la programación es como Taco Bell? ¡Siempre son los mismos ingredientes utilizados de una manera diferente! En este caso particular, vamos a confiar mucho en los Eventos para crear toda la arquitectura de la aplicación. ## ¿Por qué necesitamos Flux? @@ -30,7 +27,7 @@ Aquí hay una lista de todas las ventajas de usarlo: ![React Flux](https://github.com/breatheco-de/content/blob/master/src/assets/images/aa1a5994-8de9-4d24-99ce-3a0d686c30bd.png?raw=true) -### Flux divide la aplicación en 3 capas: +### Flux divide la aplicación en 3 capas |  |  | |:-----------|:----------------| @@ -40,101 +37,208 @@ Vistas/Views (Components) |Cada componente React que llama a cualquier acci ## Construyendo nuestra primera historia de usuario con Flux -El siguiente proyecto es una aplicación de To-Do List (lista de tareas) con 2 historias de usuario principales: +El siguiente proyecto es una aplicación de To-Do List (lista de tareas) con 3 historias de usuario principales: -+ Crear tarea (ya desarrollada y en funcionamiento). ++ Crear tarea. ++ Mostrar la lista de tareas + Eliminar tarea. -Para codificar la función para eliminar tareas, tenemos que actualizar estos archivos: (1) El Componente (para cuando el usuario haga clic), (2) las Acciones, (3) El Store (dos veces), y (4) el Componente una última vez. Son solo 3 archivos y 5 actualizaciones. Y tienes que hacer eso para cada historia de usuario que vayas a construir en tu aplicación. +Para codificar esta lista de tareas tenemos que crear 4 archivos: + +1. Un componente para agregar tarea. +3. Un componente para los items de la lista. +2. Un archivo para las actions y el estado(store). +4. El archivo principal donde integraremos todo. > *Al final, trabajar con Flux tiene que convertirse en algo tan automático como andar en bicicleta.* ![react flux](https://github.com/breatheco-de/content/blob/master/src/assets/images/77c93bfa-92cb-44e3-a7c5-c959e27c5ccc.jpeg?raw=true) -## Vamos a implementar la función eliminar tarea +## Vamos a implementar una lista de tareas -### 1) ¿Qué acción del usuario inicia la función? +### 1) Crearemos un reducer para implementar el patrón flux -(Siempre es un evento típico de JS como: hacer clic, on hover, cambiar el tamaño, etc.) +Para poder tomar el control del flujo de los datos en nuestra aplicación utilizaremos un `reducer` para agrupar las funciones y la lógica de la aplicación (actions) junto con los datos que manejan y que tienen que estar disponible para los componentes (state). -***Todo comienza cuando el usuario haga clic en el icono de la papelera. Es por eso que necesitamos iniciar nuestra aplicación escuchando el típico evento onClick en el botón de eliminar.*** +Por ahora solo diremos que el reducer es una función que genera un estado nuevo cada vez que se ejecuta y lo que haga dependerá de la información que reciba en la función `action`. Esto nos permitirá llamar a las `actions` para actualizar el estado como lo indica el patrón flux. Para entender en detalle como funciona un reducer, puedes [leer esté artículo]() donde lo explicamos a profundidad. ```javascript -// En el componente que representa cada elemento de tarea, debemos agregar un botón y también un activador onClick que llame -// a la respectiva función TodoAction.deleteTodo(task) que crearemos en las acciones: - - +// Esta es la función reducer +const TaskReducer = (state, action) => { + // Dependiendo del type de la acción realiza una tarea distinta + switch (action.type) { + case "add": + return [...state, action.payload]; + case "remove": + let newState=[...state] + newState.splice(action.index, 1); + return newState + default: + return state; + } +}; ``` -### 2) Luego necesitamos codificar nuestra acción dentro del archivo MyActions.js de esta forma: +El siguiente paso es hacer que esta función esté disponible para todos los componentes de mi aplicación, para eso utilizaremos un contexto con el hook `useReducer`, el cual nos va a permitir crear el estado y la función `actions` para ponerla a disposición del resto de la aplicación. -```javascript -MyActions.js - -// En este caso, decidimos que esta función (conocida como action) recibirá el ID de la tarea que se eliminará -class MyActions extends Flux.Actions{ - deleteTask(taskToDelete){ - // Obtiene la lista actual de acciones del store - let currentActions = MyStore.getActions(); - let updatedActions = currentActions.filter((task) => { - return (task.id != taskToDelete.id); - }); - - this.dispatch('MyStore.setActions', updatedActions); - } +```react +//TaskContext.jsx +import { useReducer, createContext } from "react"; + +// Creamos el contexto vacío +const TaskContext = createContext(null); + +const TaskReducer = (state, action) => { + // Aquí va el reducer que se definió anteriormente👆 +}; + +// Crearemos un componente que va a envolver nuestra aplicación en el contexto +export function TaskProvider({ children }) { + // Creamos el state 'tasks' y el despachador 'taskActions' + // adicionalmente pasamos como estado inicial un arreglo vacío + const [tasks, taskActions ]= useReducer(TaskReducer, []); + return ( + {/* Creamos el contexto con nuestro state y actions */} + {children} + ); } + +// Es necesario exportar el contexto para usarlo en otros componentes +export default TaskContext; ``` -> ☝ Este es un componente de clase. Te recomendamos que uses componentes funcionales y hooks en su lugar, ya que los componentes de clase están considerados como legacy (deprecados). +Ya con esto tenemos listo nuestro contexto con las tasks, ahora solo falta envolver nuestra aplicación en este componente para empezar a utilizarlo. -### 3) Actualizar el store para manejar la nueva acción enviada +```react +//index.jsx +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; +import { TaskProvider } from "./TaskContext.jsx"; + +ReactDOM.createRoot(document.getElementById("root")).render( + + + + + , +); -```javascript -// Dentro de todoStore tenemos un método HandleActions que contiene la lógica para manejar cada acción distribuida -// Tenemos que agregar un nuevo caso al switch con el nombre 'DELETE_TODO' -// Tiene que coincidir con el nombre de la acción que se envió - -handleActions(action) { - switch (action.type) { - ... - case 'DELETE_TODO': { - this.deleteTodo(action.id); - break; - } - } -} ``` +Ahora solo queda llamar al contexto desde los componentes que necesiten hacer uso de nuestras tareas. -### 4) Dentro del To-Do Store, implementa la lógica para eliminar la tarea y emitir los cambios +### 2) Empecemos por agregar una nueva tarea -```javascript +Para ello usaremos un componente que muestre una caja de texto y un botón que realiza la acción de agregar la tarea, todo dentro de un formulario para facilitar el manejo del evento de envío(submit). + +Todo esto es básico de un formulario en react, pero como queremos utilizar las `actions` del contexto, necesitamos llamarlo en el componente. -// En cualquier lugar de tu clase TodoStore, agrega un nuevo método que finalmente elimine la tarea del to-do list -// En este caso estamos usando la función de filter porque devuelve el mismo array pero solo con -// los elementos que coinciden con la pregunta lógica dentro del filtro (task.id != id) - -class TodoStore extends EventEmitter { - ... - deleteTodo(id){ - this.todos = this.todos.filter((task)=>{ - // Filtra todas las tareas que tienen el id dado - return (task.id != id); - }); - this.emit('change'); +```react +import { tasks, useContext } from "react"; +import TaskContext from "./TaskContext.jsx"; + +export default function AddItem() { + const { taskActions } = useContext(TaskContext); + // A partir de este punto tenemos disponibles las actions del reducer + // ... +} +``` + +Para hacer uso de estas `actions` se llama a la función y se le pasa como parámetro un objeto con la propiedades de la acción que queremos realizar, siendo la mas importante `type` que indica la acción especifica a ejecutar. El resto de las propiedades son datos opcionales que pueden ser requeridos por la acción. + +```react +// AddItem.jsx +import { useContext } from "react"; +import TaskContext from "./TaskContext.jsx"; + +export default function AddItem() { + // Hacemos uso del contexto y accedemos a la función 'taskActions' + const { taskActions } = useContext(TaskContext); + function handleAddTask(e) { + e.preventDefault(); + // Llamamos al actions especificándole 'type' + // asi como también la tarea que se va a agregar + let textbox = e.target.elements.task; + taskActions({ type: "add", payload: textbox.value }); + textbox.value = ""; } - ... + return ( +
  • +
    + + +
    +
  • + ); } ``` -> ☝ Este es un componente de clase. Te recomendamos que uses componentes funcionales y hooks en su lugar, ya que los componentes de clase están considerados como legacy (deprecados). +### 3) Ahora vamos a mostrar la lista + +De la misma forma como accedemos a `taskActions`, también podemos acceder al objeto `tasks` que contiene el estado con la lista. Igual que antes, debemos hacer uso de `useContext` en nuestro componente. + +```react +import { useContext } from "react"; +import "./App.css"; +import TaskContext from "./TaskContext.jsx"; +import ListItem from "./ListItem.jsx"; +import AddItem from "./AddItem.jsx"; + +export default function App() { + // Accedemos al contexto, pero esta vez solo vamos a usar 'tasks' + const {tasks} = useContext(TaskContext); + + return ( +
    +

    Todo list

    +
      + + {tasks.map((task, index) => ( + + ))} +
    +
    + ); +} +``` + +Puedes notar que aparece el componente `AddItem` que vimos previamente y desde donde se pueden agregar tarea. Luego de eso se hace el renderizado de la lista con la función `map`, pero notamos que se esta usando un componente `ListItem` para mostrar los elementos, no solo eso sino que ahi también corresponde hacer la eliminación de la tarea, veamos ese componente. + +### 4) Eliminación de items -## El Resultado +Si bien el renderizado es básico (un elemento `li` con el texto y un botón), lo interesante es como hacemos la eliminación del item con las actions. -Finalmente, tenemos una nueva función implementada en nuestro proyecto. Para seguir agregando más funciones, solo tienes que iniciar de nuevo el flujo de trabajo de codificación de Flux desde el paso 1. +***Todo comienza cuando el usuario haga clic en el icono de la papelera. Es por eso que necesitamos iniciar nuestra aplicación escuchando el típico evento onClick en el botón de eliminar.*** - +```react + onClick={() => taskActions({ type: "remove", index })} +``` -
    Haz clic aquí para abrir el demo en una nueva ventana
    +Notamos que el llamado al action es parecido al que usamos para agregar items, pero se le esta pasando un parámetro distinto llamado `index`, que le indica al dispatcher que elemento va a eliminar. Asi como vimos en ambos ejemplos, podemos pasar la data que necesite nuestra action al momento de llamarla como parámetros adicionales. + +```react +import { useContext } from "react"; +import TaskContext from "./TaskContext.jsx"; + +export default function ListItem({ task, index }) { + const { taskActions } = useContext(TaskContext); + + return ( +
  • + {task} + +
  • + ); +} +```` +## Resultado final +Ya hemos implementado la lógica de nuestra aplicación en un contexto aplicando el patrón flux, permitiendo su uso en distintos componentes. A continuación podemos ver el resultado final. + diff --git a/src/content/lesson/what-is-react-flux.md b/src/content/lesson/what-is-react-flux.md index d60ab7ca9..a6d0f6793 100644 --- a/src/content/lesson/what-is-react-flux.md +++ b/src/content/lesson/what-is-react-flux.md @@ -9,9 +9,6 @@ status: "published" --- -> ### ⚠️Warning⚠️ -> The following article has information that currently is deprecated by the React community. Instead is recommended the use of the hooks `useContext` and `useReducer` in combination to handle de app data, using native libraries, you can learn how to do this in [this article](https://4geeks.com/lesson/managin-react-app-data). The information presented in this article still may be useful for legacy systems that are currently running with this library, although is not recommended for new developments. - Remember how we always say that programming is like Taco Bell? It’s always the same ingredients, except differently! In this particular case, we are going to be relying heavily on Events to create our entire application architecture. ## Why do we need Flux? @@ -35,106 +32,194 @@ Here is a list of all the advantages of using it: |  |  | |:-----------|:----------------| Views (Components) |Every React Component that calls any Flux action is called a view. The reason to call those components differently is that React components are supposed to communicate with each other through their props (without Flux).

    Once a React Component is hard-coded to Flux, you will not be able to reuse that component in the future (on this or any other development). | -|Actions |Actions can be triggered components (when the user clicks or interacts with the application) or by the system (for example, the auto-save functionality). Actions are the first step of any Flux workflow, and they always need to dispatch to the store. | +|Actions |Actions can be triggered by components (when the user clicks or interacts with the application) or by the system (for example, the auto-save functionality). Actions are the first step of any Flux workflow, and they always need to dispatch the store. | |Store |The store contains all the application data. It handles everything incoming from the dispatcher and determines the way data should be stored and retrieved. | -## Building our first User History with Flux - -The following project is a To-Do List application with 2 main user stories: +## Building a to-do list with flux. -+ Create task (already developed and working). -+ Delete task. +### 1) Let's build a reducer that implements the flux pattern. -To code the delete functionality, we have to update these files: (1) The Component (for when the user clicks on the track), (2) The Actions, (3) The Store (two times), and (4) The Component one last time. It's only 3 files and 5 updates. And you have to do that for every user story that you are going to build into your application. +To take control over the flow of the data in our application we'll use a `reducer` to group the functions and the logic of the application (actions) along with the data that they handle and must be available to all the other components (state). -> *In the end, working with Flux has to become something as automatic as riding a bike.* +For now, let's just say that the reducer is a function that generates a new state every time it runs, and what it does depends on the information passed to the `action` function. This allows us to call the `actions` to update the state as indicates the flux pattern. To understand better how a reducer works, you can read [this article]() where we cover this in more depth. -![react flux](https://github.com/breatheco-de/content/blob/master/src/assets/images/77c93bfa-92cb-44e3-a7c5-c959e27c5ccc.jpeg?raw=true) +```javascript +// This is the reducer function +const TaskReducer = (state, action) => { + // Depending on the action type, it performs a specific action + switch (action.type) { + case "add": + return [...state, action.payload]; + case "remove": + let newState=[...state] + newState.splice(action.index, 1); + return newState + default: + return state; + } +}; +``` -## Let's Implement the Delete Task Functionality +The next step is to make this function available for all the other components of the application, for that we'll use a context with the hook `useReducer`, which will allow us to create a state and the `actions` function to publish it to the rest of the components. -### 1) What user action starts the functionality? +```react +//TaskContext.jsx +import { useReducer, createContext } from "react"; -(It’s always a typical JS event like click, hover, resize, etc.) +// Create an empty context +const TaskContext = createContext(null); -***Everything starts whenever the user clicks on the trash can icon. That is why we need to start our application by listening to the typical onClick event on the delete button.*** +const TaskReducer = (state, action) => { + // Here is the reducer defined previously👆 +}; -```javascript -// In the component that renders each to-do item we need to add a button and also an onClick listener that calls -// the respective TodoAction.deleteTodo(task) function that we will create on the actions: +// Create a component to wrap our application within the context +export function TaskProvider({ children }) { + // Create the state 'tasks' and the dispatcher 'taskActions' + // additionally we'll pass an empty array as an initial value. + const [tasks, taskActions ]= useReducer(TaskReducer, []); + return ( + {/* Create the context with our state and actions */} + {children} + ); +} - +// Is necessary to export the context for it to be used in other components. +export default TaskContext; ``` -### 2) Then we need to code our action inside the MyActions.js file like this: +Now the context is ready with our tasks, all we have to do is wrap our app with this component to start using our context. + +```react +//index.jsx +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; +import { TaskProvider } from "./TaskContext.jsx"; + +ReactDOM.createRoot(document.getElementById("root")).render( + + + + + , +); -```javascript -MyActions.js - -// In this case, we decided that this function (a.k.a action) will receive the ID of the task to be deleted. -class MyActions extends Flux.Actions{ - deleteTask(taskToDelete){ - // Get the current list of actions from the store - let currentActions = MyStore.getActions(); - let updatedActions = currentActions.filter((task) => { - return (task.id != taskToDelete.id); - }); - - this.dispatch('MyStore.setActions', updatedActions); - } -} ``` +The only thing left to do is to call the context in the components that need to use our tasks. -> ☝ This is a class component. We strongly recommend you to use functional components and hooks instead because class components are legacy. +### 2) Let's start adding a new task -### 3) Update the store to handle that new dispatched action +For that, we'll use a component that shows a textbox and a button that dispatches the adding action, all inside a form to ease the handle of the submit event. -```javascript -// Inside the todoStore we have a HandleActions method that contains the logic to handle each dispatched action -// We have to add a new case to the switch with the name 'DELETE_TODO' -// It has to match the name of the action that was dispatched - -handleActions(action) { - switch (action.type) { - ... - case 'DELETE_TODO': { - this.deleteTodo(action.id); - break; - } - } -} -``` +All that is basic stuff, but since we want to use the context `actions`, we need to call it from within the component. -### 4) Inside the To-Do Store, implement the actual logic for deleting the task and emitting the changes +```react +import { tasks, useContext } from "react"; +import TaskContext from "./TaskContext.jsx"; -```javascript +export default function AddItem() { + const { taskActions } = useContext(TaskContext); + // From this point onwards we have the reducer's actions available. + // ... +} +``` -// Anywhere on your TodoStore class, add a new method that finally deletes the task from the todo list. -// In this case we are using the filter function because it returns the same array but only with -// the elements that match the logical question inside the filter (task.id != id) - -class TodoStore extends EventEmitter { - ... - deleteTodo(id){ - this.todos = this.todos.filter((task)=>{ - // Filter all tasks that have the given id - return (task.id != id); - }); - this.emit('change'); +To make use of these `actions` we call the function passing as a parameter an object with the properties of the action we want to call, being the most important the property `type` that indicates the specific action to execute. The rest of the properties are optional data that may be required by the action itself. + +```react +// AddItem.jsx +import { useContext } from "react"; +import TaskContext from "./TaskContext.jsx"; + +export default function AddItem() { + // Using the context we access the 'taskActions' function. + const { taskActions } = useContext(TaskContext); + function handleAddTask(e) { + e.preventDefault(); + // Calling the action specifying the 'type' + // as well as task data that will be added. + let textbox = e.target.elements.task; + taskActions({ type: "add", payload: textbox.value }); + textbox.value = ""; } - ... + return ( +
  • +
    + + +
    +
  • + ); } ``` -> ☝ This is a class component. We strongly recommend you to use functional components and hooks instead because class components are legacy. +### 3) Now let's show the list + +In the same manner that we access `taskActions`, we can also access the `tasks` object that contains the state of the list. Just like before, we must use the `useContext` hook in our component. + +```react +import { useContext } from "react"; +import "./App.css"; +import TaskContext from "./TaskContext.jsx"; +import ListItem from "./ListItem.jsx"; +import AddItem from "./AddItem.jsx"; + +export default function App() { + // Accessing the context, but this time only using 'tasks' + const {tasks} = useContext(TaskContext); + + return ( +
    +

    Todo list

    +
      + + {tasks.map((task, index) => ( + + ))} +
    +
    + ); +} +``` + +You can see that we are using the `AddItem` component where you can add a task. After that is being rendered the list with a `map` function, but a component `ListItem` is being used to show this element, and it also has the functionality to remove a task, let's check out that component. -## The Result +### 4) Task deletion -Finally, we have a new functionality implemented into our project. To keep adding more functionalities, you just have to start the Flux coding workflow from step 1 all over again. +Even though the render is very basic (a `li` element with the text and a button), what's interesting is how we delete an item with the actions. - +***I all stats with the user clicking the trash can icon. That's why we need to start our component by listening to the classic onClick event on the delete button,*** -
    Click here to open demo in a new window
    +```react + onClick={() => taskActions({ type: "remove", index })} +``` + +We notice that the call to `actions` is similar to the one used to add items, but in this case is receiving a different parameter called `index`, which indicates to the dispatcher which elements are going to be deleted. Just like we saw in both examples, we can pass any data that's needed to the action at the moment of calling it, as additional parameters. + +```react +import { useContext } from "react"; +import TaskContext from "./TaskContext.jsx"; + +export default function ListItem({ task, index }) { + const { taskActions } = useContext(TaskContext); + + return ( +
  • + {task} + +
  • + ); +} +```` +## Final result +We have implemented the logic of our application in a context applying the flux pattern, allowing its use in different components. Now we can see the final result working. + \ No newline at end of file