No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Middlewares

11/22
Recursos

Aportes 25

Preguntas 4

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

Este es el middleware que se me ocurrió: Agregar el prefijo “Poke :” al nombre de cada pokemon.

export const prefix = store => next => action => {
    const prefixed = action.action.payload.map( pokemon => ({
        ...pokemon,
        name: 'Poke: ' + pokemon.name
    }) )

    const updatedAction = {
        ...action,
        action: {...action.action, payload: prefixed}
    }
    next(updatedAction);
}

Creo que ha esto se refería la profesora cuando menciona que la curva de aprendizaje es más pronunciada en Redux comparada a Context API. La sintaxis del middleware me parece bien complicada: una función que recibe como argumento otra función la cual a su vez devuelve otra función… y creo que allí termina. Seguro que la puedo memorizar pero me interesa más entenderla. ¿Sería equivalente a escribir lo siguiente?:


export const logger = function(store) {
	return function(next) {
		return function(action) {
  			console.log(action);
  			next(action);
		};
	};
};

Para los que no entendieron el concepto de Currying functions aquí un artículo, obviamente en inglés como lo está el 99.999% de la información en internet:

https://javascript.info/currying-partials

Mi middleware personalizado fue agregarle un numero indexado a cada pokemon. Aqui el codigo

export const number = (store) => (next) => (action) => {
    let arrayPokeWithNumber = [...action.action.payload] 
    for (let i = 0 ; i < arrayPokeWithNumber.length; i++) {
        arrayPokeWithNumber[i].name = `${[i]} - ${arrayPokeWithNumber[i].name}` 
    }
    const newFeature = {
        ...action,
        action:{...action.action, payload: arrayPokeWithNumber}
    }
    next(newFeature)
}

Middleware para invertir el nombre de los pokemones 😃

export const reversed = (store) => (next) => (action) => {
    const reversedPkm = action.action.payload.map((item) => {
        const split = item.name.split('');
        const reverseArray = split.reverse();
        const joinArray = reverseArray.join('');

        return { ...item, name: joinArray };
    });

    const updated = { ...action, action: { ...action.action, payload: reversedPkm } };

    next(updated);
};

Para quién venga con TS, le dejo el código que a mí me ha funcionado. Reconozco que ha faltado tipar algunas cosas, pero es porque lo estoy practicando.
.
En este caso, me cost;o entender que lo que aparece en el video, no es lo mismo a la respuesta que me estaba dando en consola cuando se mostraba información en el logger. Acá dejo el resultado:

// ** index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { pokemonsReducer } from './reducers/pokemons';
import { Provider } from 'react-redux';
import { applyMiddleware, compose, legacy_createStore as createStore } from 'redux';
import { featuring, logger } from './middlewares';
import './index.css';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);

const composedEnhancers = compose(
  applyMiddleware(logger, featuring),
  (window as any).__REDUX_DEVTOOLS_EXTENSION__ && (window as any).__REDUX_DEVTOOLS_EXTENSION__(),
)

const store = createStore(pokemonsReducer, composedEnhancers);

root.render(
  <Provider store={store}>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </Provider>
);

.

// ** Middlewares/index.ts

export const logger = (store: any) => {   // store de la aplicación
  return (next: any) => {          // next es una función que se llama cuando el middleware termina su trabajo y envía el action al reducer
    return (action: any) => {      // action es la información que se pasa al reducer
      console.log(action);
      next(action); // <-- hace que el action llegue al reducer
    } 
  }
};

export const featuring = (store: any) => {
  return (next: any) => {
    return (actionNew: any) => {
      debugger;
      const featured = [
        { name: 'Momea', url: 'https://ab.c' }, // <-- nuevo pokemon que quiero meter al state
        ...actionNew.payload, // <-- desestructura todos los pokemons que están en el payload de 'action'
      ];
      const updatedAction = {
        ...actionNew,
        payload: [
          ...featured,
        ]
      }
      next(updatedAction);
    } 
  }
};

No tengo el mismo objeto, por eso en el middleware cambié la forma de desestructurar todo el array

la funcionalidad de mi middleware personalizado fue ordenar el arreglo de pokemones en orden alfabético a partir de la propiedad name de cada uno

export const myMiddleware = (store) => (next) => (actionInfo) => {
    const alphabetPokemon = actionInfo.action.payload.map(poke => poke.name).sort()
    const pokemonPayload = actionInfo.action.payload.map((poke, index) => ({ ...poke, name: alphabetPokemon[index] }))

    const myFormatPokemon = {
        ...actionInfo,
        action: { ...actionInfo.action, payload: pokemonPayload}       
    }

    // console.log(myFormatPokemon)
    next(myFormatPokemon)
}

Middlewares:
Es una pieza de código que se ejecuta cuando X recibe un request y ese mismo X da respuesta al request.

Ayuda a los desarrolladores a diseñar aplicaciones con mayor eficiencia. Además, actúa como hilo conductor entre las aplicaciones, los datos y los usuarios.

Los podemos usar para:

  • Hacer logs de errores

  • Hacer fetch de data

  • Depurar nuestra aplicación

  • También podemos customizar nuestra data con applyMiddleware

Agregue una nueva propiedad dentro del arreglo de pokemons con el numero del pokemon con el formato #001

export const enumerated = (store) => (next) => (action) => {
    const list = [ ...action.action.payload];    
    const listUpdated = [];
    let count = 0;    
    list.forEach( (pokemon) => listUpdated.push({...pokemon, number: "#" + (++count).toString().padStart(3,'0') }));
    const updatedAction = {...action, action: {...action.action, payload: listUpdated}}
    next(updatedAction);
};

Si están trabajando con ts, encontré esta solución y, hasta el momento parece funcionar
https://www.npmjs.com/package/redux-devtools-extension

Hice un middleware que pone la primera letra del nombre del Pokémon en mayúscula.

export const nameUpperCase = (store) => (next) => (actionInfo) => {

    const featured = [
         ...actionInfo.action.payload.map(
            pokemon => ({
                ...pokemon,
                 name: pokemon.name.charAt(0).toUpperCase() + 
                        pokemon.name.slice(1)
            })
        )
    ]

    const updateActionInfo = {
        ...actionInfo,
        action: {
            ...actionInfo.action,
            payload: featured
        }
    }
    next(updateActionInfo)
}

MI middleware que agrega un valor aleatorio de poder para cada pokemon

export const addPower = (store: any) => (next: any) => (actionInfo: any) => {

  const featured = actionInfo.payload.map((e: any) => ({ ...e, "power": Math.floor(Math.random() * 100) + 1 }))
  const updatedAction = {
    ...actionInfo, payload: featured
  }
  next(updatedAction)
}

mi middleware

Reto del Middleware:

Poner los nombres de los pokemos en mayúscula:

¡Y este fue el resultado! 🎉

El compañero @CésarPalma dejó el siguiente comentario que me pareció perfecto para entender un poco mejor…

En programación funcional, a esto se le llama hacer currying.
https://yeisondaza.com/currying-en-javascript-funciones-con-superpoderes

Agregar la imagen del pokemon:

export const addImageToPokemons = (store) => (next) => async (action) => {
    let pokemons = action.action.payload

    for (let i = 0; i < pokemons.length; i++) {
        const element = pokemons[i];
        let data = await getImagePokemon(element.name)
        let image = data.data.sprites.front_default

        element.image = image
    }

    next(action)
}

¿Esto esta bien implementado? Porque no necesito pasar la copia del estado 😕

Me esta gustando mucho este curso. La profesora se deja entender muy bien.

Hice un middleware que elimina un pokemon random de todos los que están disponibles, deje console.log para que puedan ver cuál fue el seleccionado y como quedan los pokemons sin ese, aqui esta el middleware ==>

export const removeAnyPokemon = (_store) => (next) => (action) => {
    if (action.action.payload.length === 0) {
        return next(action);
    };

    const numberIdToDelete = Math.floor(Math.random() * action.action.payload.length);
    const pokemonToEliminate = action.action.payload[numberIdToDelete];

    console.log('Selected Pokemon Number and this Pokemon', numberIdToDelete, pokemonToEliminate);
    console.log('Array and its length before removing', action.action.payload, action.action.payload.length);
    
    action.action.payload.splice(numberIdToDelete, 1);
    
    console.log('Array after deletion and its length',action.action.payload, action.action.payload.length);
    
    next(action);
};

Con Redux Toolkit es 1000 veces más sencillo implementar todos estas funciones. Entiendo que es bueno conocer la forma vieja de hacerlo, pero la explicación está un poco mareada, es muy fácil perderse.

Este fue mi middleware

import { Middleware, Dispatch, Action } from 'redux';

const logger: Middleware = ({ getState }: any) => (next: Dispatch) => (action: Action) => {
  console.log('will dispatch', action);
  const returnValue = next(action);
  console.log('state after dispatch', getState());
  return returnValue;
};

const modifyName: Middleware = ({ getState }: any) => (next: Dispatch) => (action: {type:string,payload:any}) => {
    const payload = action.payload;
    payload.forEach((element: {name:string,url:string}) => {
        element.name = `Name: ${element.name}`
    });
    const returnValue = next(action);
    return returnValue;
};

const middleware = {logger,modifyName};
export default middleware;

Yo utilicé un Middleware para poner la primera letra de cada nombre en mayúscula.

export const upperCaseFirstLetterName = (store) =>(next) => (actionInfo) => {
    
    const pokemonsUp = actionInfo.payload.map(pokemon=>{
        
        const upperPok = pokemon.name.charAt(0).toUpperCase() + pokemon.name.slice(1)
        return {name: upperPok}
    })

    
    const updatedPayload = {
        ...actionInfo,
        payload: pokemonsUp
    }
    next(updatedPayload)
}

Currying es una técnica de programación funcional que permite transformar una función que toma múltiples argumentos en una secuencia de funciones que toman un solo argumento cada una.

El objetivo de currying es hacer que las funciones sean más reutilizables y componibles, permitiendo a los desarrolladores crear funciones más específicas a partir de funciones más generales.

Mi middleware personalizado para agregarle una propiedad adicional “isActive” a cada pokemon:

export const addActivationProp = (store) => (next) => actionInfo => {
    const newPayload = actionInfo.action.payload.map(pokemon => pokemon.isActive = true);
    const newActionInfo = { ...actionInfo, payload: newPayload};
    next(newActionInfo);
}

El middeware que cree fue pokemonsWithId que agrega el id a los pokemons.

import types from '../actions/types';

const pokemonsWithId = (store) => (next) => (actionInfo) => {
  if (actionInfo.action.type === types.setPokemons) {
    const pokemons = actionInfo.action.payload;
    const pokemonsWithId = pokemons.map((pokemon, i) => ({ ...pokemon, id: i + 1 }));
    
    const updatedAction = {
      ...actionInfo,
      action: { ...actionInfo.action, payload: pokemonsWithId },
    };
    next(updatedAction);
  } else {
    next(actionInfo);
  }
};

export default pokemonsWithId;

Para usar los devtools y el middleware lo hice de la siguiente manera, use un paquete

npm install --save-dev redux-devtools-extension

import { composeWithDevTools } from "redux-devtools-extension";

const middlewareEnhancer = applyMiddleware(logger);

const store = createStore(
  pokemonsReducer,
  composeWithDevTools(middlewareEnhancer)
);

funciona perfecto por si alguno tiene un error y está usando TypeScript