Por si a alguien le pasa como a mi que no puede instalar la dependencia, recuerden que es “immutable” con doble “m” y no “inmutable” 😅
Conceptos claves para empezar
¿Ya tomaste el Curso Básico de Redux?
Conceptos claves de Redux
Ciclo de vida de Redux
Diferencias entre Redux y Context
Introducción a nuestro proyecto
Creemos una Pokedux
Iniciando nuestro proyecto
¡Atraparlos ya!
Introducción a PokeAPI
React.js + Redux
Integrando Redux
Hooks vs. Connect
Redux DevTools
Middlewares
Middlewares
Peticiones asíncronas
Redux Thunk
Middlewares alternativos: Redux Saga
Avanzando la ui
Agreguemos un loader
Agreguemos favoritos
Inmutabilidad
¿Qué es inmutabilidad?
Agregando Inmutabilidad a nuestra Pokedux
Avanzado
Cuándo usar reducers combinados
Redux Toolkit: creando nuestro primer Slice
Redux Toolkit: createAsyncThunk
Despedida del curso
Conclusiones
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Mariangélica Useche
Aportes 24
Preguntas 3
Por si a alguien le pasa como a mi que no puede instalar la dependencia, recuerden que es “immutable” con doble “m” y no “inmutable” 😅
Creo que Mariangélica tiene un error de código, pero corríjanme si me equivoco.
Yo NO vi el loader en su app y creo que tiene que ver con que ella puso el action.payload de SET_LOADING dentro del array.
Yo lo puse afuera:
case SET_LOADING:
return state.setIn(['loading'], action.payload);
Y a mí sí me muestra el loading así.
En mi caso, por defecto se instaló “immutable”: “^4.1.0”, a diferencia de la 4.0 de la clase, esto requirió cambios en el reducer y en el App. js:
pokemon.js
switch (action.type) {
case SET_POKEMONS:
// action.action.payload, porque uso un midleware q modifica el listado
return setIn(state, ['pokemons'], fromJS(action.action.payload));
case SET_LOADING:
return setIn(state, ['loading'], action.payload);
case SET_FAVORITE:
const currentPokemonIndex = get(state, 'pokemons').findIndex(
(pokemon) => pokemon.get('id') === action.payload.pokemonId
);
if (currentPokemonIndex < 0){
return state;
}
const isFavorite = getIn(state, ['pokemons', currentPokemonIndex, 'favorite']);
return setIn(state, ['pokemons', currentPokemonIndex, 'favorite'], !isFavorite);
default:
return state;
}
App.js
const pokemons = useSelector(state => get(state, 'pokemons')).toJS();
const loading = useSelector((state) => get(state, 'loading'));
Hice el código, pero la verdad me parece que es mucho mejor trabajar con js nativo, siento que tengo más control y encima la librería complica mucho más lo que ya está hecho, asi lo siento yo.
Me agrada la propuesta, pero prefiero que todo sea más Vanilla. Ayuda a que seamos un poco mejores con el código y cómo nos va cuando manipulamos las cosas… En este caso es una clase y se puede practicar, pero siento que cuando se tiene claro el uso de inmutabilidad por Vanilla JS, se puede lanzar con librerías y otros recursos.
Con la version 4.1.? de immutable quedaría de la siguiente forma
import { fromJS, setIn, getIn} from 'immutable';
...
case SET_POKEMONS:
//return { ...state, pokemons: action.payload };
//return state.setIn(['pokemons'], fromJS(action.payload))
return setIn(state, ['pokemons'], fromJS(action.payload))
valida la información , pero me parece más complicado que el método nativo de js spread operator
npm install immutable
npm i immutable
import {SET_FAVORITE,SET_LOADING,SET_POKEMON} from "../actions/types.js";
import {fromJS, get, getIn, setIn} from "immutable";
const initialState = fromJS({
pokemons: [],
loading:false,
favorites:[]
});
export const pokemonsReducer = (state,action) => {
console.log(state)
switch (action.type) {
caseSET_POKEMON:
//return {...state, pokemons: action.payload}
return setIn(state,['pokemons'],fromJS(action.payload));
break;
caseSET_FAVORITE:
const currentPokemonIndex = get(state,'pokemons').findIndex(
(pokemon) => {
returnpokemon.get('id') ==action.payload.pokemonId;
}
);
if (currentPokemonIndex < 0) {
returnstate
}
//const isFavorite = state.get('pokemons').get(currentPokemonIndex).get('favorite');
const isFavorite = getIn(state,['pokemons',currentPokemonIndex,'favorite']);
return setIn(state,['pokemons',currentPokemonIndex,'favorite'],!isFavorite);
break;
caseSET_LOADING:
return setIn(state,['loading'],action.payload);
break;
default:
return {...state};
break;
}
}
import {useEffect} from 'react'
import {useDispatch, useSelector} from "react-redux";
import {Col, Spin} from 'antd';
import Searcher from "./components/Searcher";
import PokemonList from "./components/PokemonList.jsx";
import {getPokemon} from "./api";
import {getPokemonWithDetails, setLoadings} from "./actions";
import Logo from "./assets/logo.svg";
import {get} from "immutable";
import './App.css'
function App() {
const pokemons = useSelector(state => get(state,'pokemons'))?.toJS();
const loading = useSelector(state => get(state,'loading'));
const dispatch = useDispatch();
useEffect(() => {
const fetchPokemons = async () => {
dispatch(setLoadings(true))
const response = await getPokemon();
dispatch(getPokemonWithDetails(response));
dispatch(setLoadings(false))
}
fetchPokemons();
}, []);
return (
<div className="App">
<Col span={4} offset={10}>
<img src={Logo} alt="pokeapi"/>
</Col>
<Col span={8} offset={8}>
<Searcher/>
</Col>
{loading ? (
<Col offset={12} className="Spinner">
<Spin spinning size="large"/>
</Col>
) : (
<PokemonList pokemons={pokemons}/>
)}
</div>
)
}
export default App;
Recomiendo ampliamente el uso de la librería **“immer” **en ves de el uso de “immutable”, puesto que es mucho más facil de utilizar y no necesitas de más complejidad que el código basico para el uso de redux, es decir, no necesitas metodos get o set u setIn,
Es muy facil de utilizar ya que solo necesitamos usar su método produce para que realice la inmutabilidad por su cuenta.
Documentación / guía paso a paso del uso de immer:
https://immerjs.github.io/immer/
Para la siguiente clase y el uso de combineReducers, se necesita instalar “redux-immer”. Ésta es su guía de implementación:
https://github.com/salvoravida/redux-immer
Dejé comentado el inicial por si acaso :v
//--return { ...state, loading: action.payload };
return state.setIn(["loading"], action.payload);
en reducers/pokemons.js
case SET_LOADING:
// return {...state, loading: action.payload}
return state.setIn(["loading"], fromJS(action.payload))
en app.js agregamos la siguiente línea de código para loading
const loading = useSelector(state => state.get("loading"));
Curioso ver cómo la app funciona en los vídeos pero misteriosamente el código no es el mismo que aparece en los ficheros del final…
Despues de muchos intentos les paso lo que hice para que me ande, utilice mucha ayuda de los comentarios y google y me quedo asi
package.json
Me quede con esta version porque era la mas descargada en npm
"immutable": "4.3.4",
App.js
/* eslint-disable react-hooks/exhaustive-deps */
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Col, Spin } from 'antd';
import Searcher from './components/Searcher';
import PokemonList from './components/PokemonList';
import logo from './statics/logo.svg'
import { getPokemon } from './api';
import { getPokemonsWithDetails, setLoading } from './actions';
import './App.css';
import { get } from 'immutable'
function App() {
const pokemons = useSelector(state => get(state, 'pokemons')).toJS()
const loading = useSelector(state => get(state, 'loading'))
const dispatch = useDispatch()
useEffect(() => {
const fetchPokemons = async() => {
dispatch(setLoading(true))
const pokemonsRes = await getPokemon()
dispatch(getPokemonsWithDetails(pokemonsRes))
}
fetchPokemons()
}, [])
return (
<div className="App">
<Col span={4} offset={10}>
<img src={logo} alt="Pokedux"/>
</Col>
<Col span={8} offset={8}>
<Searcher/>
</Col>
{loading ? <Col offset={12}>
<Spin spinning size="large"/>
</Col> : <PokemonList pokemons={pokemons}/> }
</div>
);
}
export default App
El reducer pokemons.hjs
import { fromJS, get, getIn, setIn } from 'immutable'
import { SET_FAVORITE, SET_LOADING, SET_POKEMONS } from "../actions/types"
const initialState = fromJS({
pokemons: [],
loading: false,
})
export const pokemonsReducer = (state=initialState, action) => {
switch(action.type) {
case SET_POKEMONS:
return setIn(state, ['pokemons'], fromJS(action.payload))
case SET_FAVORITE:
const currentPokemonIndex = get(state,'pokemons')
.findIndex((pokemon) => {
return get(pokemon, 'id') === action.payload.pokemonId
} )
if (currentPokemonIndex === -1) return state
const isFavorite = getIn(state, ['pokemon', currentPokemonIndex, 'favorite'])
return setIn(state, ['pokemosn', currentPokemonIndex, 'favorite'], !isFavorite)
case SET_LOADING:
return setIn(state, ['loading'], fromJS(action.payload))
default:
return state
}
}
me quedo con el spread operator…
Para resolver el reto hice lo siguiente
Si les sale la advertencia
npm WARN @apideck/better-ajv-errors@0.3.6 requires a peer of ajv@>=8 but none is installed. You must install peer dependencies yourself.
npm WARN fork-ts-checker-webpack-plugin@6.5.2 requires a peer of typescript@>= 2.7 but none is installed. You must install peer dependencies yourself.
npm WARN tsutils@3.21.0 requires a peer of typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta but none is installed. You must install peer dependencies yourself.
al instalar immutable, no se preocupen. Para saltarsela solo deben agregar --save-dev al instalar.
Mi solución del lado de los reducers fue:
return state.setIn(['loading'], fromJS(action.payload))
Y del lado de App.js:
const loading = useSelector((state) => state.get('loading'));
case SET_LOADING:
return state.set('loading', action.payload );
/****************/
const loading = useSelector((state) => state.get('loading'));
Esta un poco diferente mi solución en el reducer, pero funcionó.
// ./reducers/pokemon
case SET_LOADING:
return state.setIn(['loading'], action.payload);
// ./app
const loading = useSelector(state => state.get('loading'));
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?