No tienes acceso a esta clase

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

No se trata de lo que quieres comprar, sino de quién quieres ser. Aprovecha el precio especial.

Antes: $249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

13 Días
17 Hrs
44 Min
14 Seg
Curso de React Router 5 y Redux

Curso de React Router 5 y Redux

Oscar Barajas Tavares

Oscar Barajas Tavares

Debuggeando nuestro proyecto (agregando validaciones a nuestro componente card)

15/29
Recursos

Aportes 56

Preguntas 2

Ordenar por:

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

Si quieres evitar que el mismo elemento se agregue muchas veces a la lista de favoritos:

reducers/index.js

    case 'SET_FAVORITE':
      const exist = state.mylist.find(item => item.id === action.payload.id)
      if (exist) return {...state}

      return {
        ...state,
        mylist: [...state.mylist, action.payload]
      }

Hay otro bug y es que una vez se agrega una película a la lista de mis favoritos ya no debe existir la opción de añadirla nuevamente.

Recordemos que JS nos permite modificar los estilos de los elementos HTML
Para solucionar este bug agregue unas lineas en el método handleSetFavorite y **handleDeleteFavorite **, los cuales ahora removerán y agregaran el display de none, respectivamente.

  const { id, cover, title, year, contentRating, duration, isList } = props;
  const handleSetFavorite = () => {
    props.setFavorite({ id, cover, title, year, contentRating, duration });
    document.getElementById(id).style.display = 'none';
  };
  const handleDeleteFavorite = (itemId) => {
    props.deleteFavorite(itemId);
    document.getElementById(id).style.display = 'inline';
  };

Adicionalmente, debemos agregar el atributo ID en cada imagen (plus and delete icon):

          {isList ? (
            <img
              className='carousel-item__details--img'
              src={removeIcon}
              alt='Remove Icon'
              onClick={() => handleDeleteFavorite(id)}
            />
          ) :
            (
              <img
                className='carousel-item__details--img'
                src={plusIcon}
                alt='Plus Icon'
                onClick={handleSetFavorite}
                id={id}
              />
            )}

Otro bug que también hay (no sé si lo corrijan después) es que puedes agreagar el mismo video varias veces al myList, y se muestra varias veces.

La propia consola te manda un error que el key se repite

Para resolver el bug de ya no volver agregar el mismo item lo resolví así.

Existe otro bug, un item añadido actualmente a la lista, no puede volver a añadirse

He visto varias soluciones para el bug de “agregar una pelicula más de una vez a la lista de favoritas”.

Esta es mi propuesta.
Busco el índice (findIndex) del item que se va a agregar dentro de la lista myList.
Si el índice no existe (-1) agrego el payload, sino, regreso el state original,

case 'SET_FAVORITE':
            const index = state.myList.findIndex(items => items.id == action.payload.id);
            if(index == -1)
                return {
                    ...state,
                    myList: [...state.myList, action.payload]
                }
            return state;

Si jugaron un poco con las cartas del video, habrán visto que puedes agregar dos veces el mismo favorito. Para solucionarlo, hice esto.
Declaré una constante fuera del switch, que se encargará de saber si existe o no, y coloqué la condicion en el return.

const exists = state.mylist.find((item) => item.id === action.payload.id);

Y el return de ** SET_FAVORITE ** es

return exists ? state : {
        ...state,
        mylist: [...state.mylist, action.payload],
      };

Yo resolví así el bug de no añadir más de una vez el mismo elemento

 case 'SET_FAVORITE':

      return state.myList.some((items) => items.id === action.payload.id) ? { ...state } :
        {
          ...state,
          myList: [...state.myList, action.payload],
        };

Me parece que para la validación de si el elemento existe quedaría más simple de esta forma

case 'SET_FAVORITE':
    const item = state.myList.find( list => list.id === action.payload.id )
    if(item) return state;
    return {
        ...state,
        myList: [...state.myList, action.payload]
    }

Yo modifiqué el reducer SET_FAVORITE.

case 'SET_FAVORITE':
      if (state.myList.filter((element) => element.id == action.payload.id).length !== 0) {
        return state;
      }
      return {
        ...state,
        myList: [...state.myList, action.payload],
      };

Para evitar agregar el mismo elemento varias veces a la lista de favoritos se puede usar en reducer/index.js, lo siguiente:

Este Profesor sin duda está entre los mejores de Platzi, no lo dejen ir y que por favor haga mas cursos

Se sigue añadiendo infinitamente desde trends. Para arreglarlo en src/reducer/index.js

const exists = state.myList.find((item) => item.id === action.payload.id);
      exists;

      return exists
        ? { ...state }
        : {
            ...state,
            myList: [...state.myList, action.payload],
          };```

Yo lo hice así:

case 'SET_FAVORITE':
      return state.mylist.find(item => item.id === action.payload.id)
        ? state
        : {
            ...state,
            mylist: [...state.mylist, action.payload],
          };

Existe un "bug " , porque no necesariamente se puede ver así y es que si añades un elemento a los favoritos , no debería mostrarse en la lista de trends o de originals . Lo resoli así :

import {
    actions
} from "../actions/index";

const reducer = (state, action) => {
 
    switch (action.type) {
        case actions.setFavorite:

            if (state.trends.find(items => items.id === action.payload.id)) {
                return {
                    ...state,
                    trends: state.trends.filter(items => items.id !== action.payload.id),
                    mylist: [...state.mylist, action.payload],
                }
            } else {
                return {
                    ...state,
                    originals: state.originals.filter(items => items.id !== action.payload.id),
                    mylist: [...state.mylist, action.payload],
                }
            }

            case actions.deleteFavorite:
                return {
                    ...state,
                    mylist: state.mylist.filter(items => items.id !== action.payload),
                        trends: [...state.trends, action.payload]
                }
                    default:
                        return state;
    }

};

export default reducer;

Para solucionar el bug que permitía agregar el mismo item varias veces a ‘Mi lista’, he creado estas dos funciones en el Home.jsx justo antes del return del componente, una para trends y otra para originals, lo que hacen es comparar con myList y sacar los elementos repetidos

const trendsWhitOutMyList = trends.filter(
    ({ id: id1 }) => !myList.some(({ id: id2 }) => id2 === id1),
  );
  const originalsWhitOutMyList = originals.filter(
    ({ id: id1 }) => !myList.some(({ id: id2 }) => id2 === id1),
  );

Ademas hay que cambiar los map

<Categories title='Tendencias'>
        <Carousel>
          {trendsWhitOutMyList.map((item) => (
            <CarouselItem key={item.id} {...item} />
          ))}
        </Carousel>
      </Categories>

      <Categories title='Originales de Platzi Video'>
        <Carousel>
          {originalsWhitOutMyList.map((item) => (
            <CarouselItem key={item.id} {...item} />
          ))}
        </Carousel>
      </Categories>```

Esta es otra forma de evitar que dos elementos se agreguen a la lista dos veces

if(check[0] !== undefined ){
              let band 
              band = false
                for(  let i = 0; i < Object.keys(check).length  ; i++){
                        console.log (check[i].id , action.payload.id)
                        if(check[i].id === action.payload.id){
                     
                         band = true
                     }
                    if(band === true){
                            return state
                        }
                    band = false
                }  
            }

Creo que es buena opción hacerlo ya que elimina la redundancia de agregarlo si ya estaba agregado, pero el usuario sigue siendo capaz de agregarlo desde la lista de tendencias

case "SET_FAVORITE":
      const alreadyExists = state.myList.filter(
        (i) => i.id === action.payload.id
      );
      if (alreadyExists.length > 0) {
        return state;
      } else {
        return {
          ...state,
          myList: [...state.myList, action.payload],
        };
      }

      break;

Yo si había visto ese bug y lo corregí de la siguiente forma

const reducer = (state, action) => {
    switch (action.type) {
        case "SET_FAVORITE":
            return {
                ...state,
                myList: [...state.myList.filter(item => item.id !== action.payload.id), action.payload]
            }

Pero claro, seguía mostrando el botón de agregar a favoritos, es mejor la solución del profe xD

Falto validar esto:

La clase anterior vi el bug que se resolvió en esta clase y para mi sorpresa lo resolvieron en esta clase. Espero que más adelante se resuelva el bug que pongo en la foto… aun así lo repararé a mi modo igual que en la clase pasada cuando no sabía que se iba a arreglar el bug de esta clase

En nuestra aplicacion podemos presentar de forma infinita distintos elementos, esto no debe de ser asi. Por eso vamos a crear una validacion que nos permite identificar que estamos en la lista de agregados y quitar el boton de agregar.

Primero le agregaremos un nuevo valor , que preguntara que si esta en la lista, esto solo lo haremos a las tarjetas que invocamos directamente a MyList entonces quedaria asi nuestro elemento al usar el MAP:

{mylist.map((item) => <CarouselItem key={item.id} {...item} isList/>)}

Asi el valor se pasara como Trrue, entonces en nuestro ITEM solo tenemos que verificar si este valor es false o true. Esto con un valor ternario

{isList
        ?<img className="carousel-item__details--img" src={deleteicon} alt="Delete Icon" onClick={() => handleDeleteFavorite(id)} />
        :<img className="carousel-item__details--img" src={plusicon} alt="Plus Icon" onClick={handleSetFavorite} /> 
        }

Y ya quedaria listo.

Esta es mi solución al problema de que el mismo elemento se pueda agregar a favoritos muchas veces:


function CarouselItem(props) {
  const { item, myList } = props;
  const { setFavorite, removeFavorite } = props;
  const inFavorites = myList.find((el) => el === item);
  const handleFavorite = () => {
    if (inFavorites) {
      removeFavorite(item.id);
    } else {
      setFavorite(item);
    }
  };
  return (
    <div className="carousel-item">
      <img className="carousel-item__img" src={item.cover} alt="" />
      <div className="carousel-item__details">
        <div>
          <img
            className="carousel-item__details--img"
            src={playIcon}
            alt="Play Icon"
          />
          <img
            className="carousel-item__details--img"
            src={inFavorites ? removeIcon : plusIcon}
            alt="Plus Icon"
            onClick={handleFavorite}
          />
        </div>
        <p className="carousel-item__details--title">{item.title}</p>
        <p className="carousel-item__details--subtitle">
          {item.year} {item.contentRating} {item.duration} minutos
        </p>
      </div>
    </div>
  );
}
const mapDispatchToProps = {
  setFavorite: setFavorite,
  removeFavorite: removeFavorite,
};
const mapStateToProps = (state) => {
  return {myList: state.myList}
};

export default connect(mapStateToProps, mapDispatchToProps)(CarouselItem);



Otro enfoque, y que a mí me parece mejor, es separar este código en una función independiente, por ejemplo mainActionButton, la cual se encarga de validar el boton a mostrar y devolver el resultado.


  const mainActionButton = () => {
    if (isFavorite) {
      return <img className='carousel-item__details--img' src={removeIcon} alt='Trash Icon' onClick={handleDeleteFavorite} />;
    }

    return <img className='carousel-item__details--img' src={plusIcon} alt='Plus Icon' onClick={handleAddFavorite} />;
  };

  return (
    <div className='carousel-item'>
      <img className='carousel-item__img' src={cover} alt={title} />
      <div className='carousel-item__details'>
        <div>
          <img className='carousel-item__details--img' src={playIcon} alt='Play Icon' />
          {mainActionButton()}
        </div>
        <p className='carousel-item__details--title'>{title}</p>
        <p className='carousel-item__details--subtitle'>
          {`${year} ${contentRating} ${duration}`}
        </p>
      </div>
    </div>
  );

escribi esta pequeña validacion para cambiar el simbolo de agregar a la lista por el de borrar tanto en la lista como en el original, myList lo tomo del store.Pero me parece algo ineficiente.

const isInMyList = () => props.myList.some(item => item.id === id);
{isInMyList()?
                <img 
                    className="carousel-item__details--img" 
                    src={removeIcon} 
                    alt="Minus Icon"
                    onClick={()=>handleDeleteFavorite(id)}
                />
                :<img 
                    className="carousel-item__details--img" 
                    src={plusIcon} 
                    alt="Plus Icon"
                    onClick={handleSetFavorite}
                />}

Brutal, jajaja no me di cuenta del bug jaja

Para eliminar el item que se añade a favoritos de las otras listas:

case "SET_FAVORITE":
      return state.mylist.find(item => item.id === action.payload.id)
        ? state
        : {
            ...state,
            mylist: [...state.mylist, action.payload],
            trends: state.trends.filter(item => item.id !== action.payload.id),
            originals: state.originals.filter(item => item.id !== action.payload.id),
          };

Que buena clase para validar los componentes, vamos por mas!

Para que se cambie el botón de añadir por el de borrar en los elementos que están en favoritos en la lista de trends:

        <Carousel>
          {trends.map(item => {
            let isList = false;
            if (myList.length > 0)
              isList = myList.reduce(favourite => favourite.id !== item.id);

            return <CarouselItem key={item.id} {...item} isList={isList} />;
          })}
        </Carousel>

Para originals sería exactamente igual solo que hay que mapear el array originals.

aun asi tenemos otro Bug y es que podemos añadir varias veces el mismo elementos desde tendencias o originales a myList

estupendo ufff, que lindo es aprender, igual mi cabeza exploto ajajajja

con esto y la bandera para que en myList no salga el icono de add evitamos agregar el mismo elemento mas de una vez

case 'SET_FAVORITE':
            
            return {
                ...state,
                myList: [
                    ...state.myList,
                    ...state.myList.map(i=>i.id).indexOf(action.payload.id)===-1?[action.payload]:[]
                ]
            }

isList, es mas que una simple variable, puedes colocare, pollito, siexiste, mesirve, etc, … es solo un identificador del “item” si existe, ya después vamos a CarouselItems.jsx, para validar “isList” si existe o no 😄

Justo en la clase pasado comenté como había resuelto este bug, y me alegra saber que fue muy parecido al del profesor. Me lo puse como reto antes de llegar a esta clase porque creo que así se demuestra lo que se está aprendiendo !! 😄

Sigue siendo posible añadir un item infinitamente desde el carrousel de tendencias u originales. Además los items que estan en myList no pueden ser eliminados desde otros carrousels

XD Ya había resuelto este problema en el video de la clase pasada! Se que hay distintas formas de resolver un problema, sin embargo mi solución fue exactamente la misma que la del profe! 😄

Si como a mí no te salió el icono, sólo tienes que especificar que isList es true explícitamente en el componente de Home, de esta forma:

isList={true}

no estoy seguro qué es payload 🤔

Les comparto mi fragmento de validación:

{
            isList ?
              (
                <img
                  className='carousel-item__details--img'
                  src={removeIcon}
                  alt='Remove Icon'
                  onClick={() => handleDeleteFavorite(id)}
                />
              ) :
              (
                <img
                  className='carousel-item__details--img'
                  src={plusIcon}
                  alt='Plus Icon'
                  onClick={handleSetFavorite}
                />
              )
          }

Mi implemenacion para evitar que se agreguen repetidos, me di cuenta que si en el “else” no regresaba el “…state” todo se rompia

		case 'SET_FAVORITE':
			if ( state.mylist.filter((items) => items.id === action.payload.id).length === 0) {
				return {
					...state,
					mylist: [...state.mylist, action.payload],
				};
			} else {
				return {
					...state,
				};
			}```

Hola, alguien me puede explicar en que momento es isList se vuelve falso (teniendo en cuenta que su estado inicial es true)? para saber que ya no debe mostrar el botón de agregar sino el de borrar?

That’s how I stay!!






.

Había notado el bug y yo lo corregi de la siguiente manera:

const reducer = (state, action) => {
    switch (action.type) {
        case 'SET_FAVORITE':
            return {
                ...state,
                myList: (!state.myList.some(item => item.id === action.payload.id)) ? [...state.myList, action.payload] : state.myList
            }
        case 'DELETE_FAVORITE':
            return {
                ...state,
                myList: state.myList.filter(item => {
                    return item.id !== action.payload
                })
            }
        default:
            return state;
    }
};

export default reducer;

Yo le agregué una validación para que no se repitan los elementos de myList y además que muestre una alerta sencilla que diga que el elemento ya se encuentra añadido en myList:

En mi caso tuve que hacer una condicion adicional en el Home para realizar esta condición:
😃

nameList.map((category) => (
          categories[category].length > 0 && (
            <Categories key={category} title={category}>
              <Carousel>
                {categories[category].map((video) => (
                  (category === 'myList') ?
                    (<Item key={video.id} {...video} isFavorite />) :
                    (<Item key={video.id} {...video} />)
                ))}
              </Carousel>
            </Categories>
          )
        ))

Para poner un ícono diferente cuando el elemento está en mi lista, podemos utilizar redux, solo necesitamos añadir una propiedad extra en nuestro initialState, y en los reducers trabajarla:

Yo lo hice asi

        <div>
          <img src={Play} alt='Play' />
          { !isList && <img src={Plus} alt='Plus' onClick={handleSetFavorite} />}

          { isList && <img src={Delete} alt='Delete' onClick={() => handleDeleteFavorite(id)} />}

        </div>

Yo lo había resuelto de esta forma. En vez del atributo isList, le puse favorite. Luego validé si era favorite mostrará uno u otro botón e implementará una u otra lógica.

<img
            className="carousel-item__details--img"
            src={favorite ? minusIcon : plusIcon}
            alt="Plus Icon"
            onClick={favorite ? handleRemoveFavorite : handleSetFavorite}
          />

Algo tranquilo después de un tren de información.

No me gustó la “validación” que hicieron en esta clase.
Lo que yo hice fue pasarle a CarouselItem dos nuevos props: showPlusIcon y showRemoveIcon.
showRemoveIcon se lo paso nada más a los CarouselItems de “Mi lista”, y dependiendo si es true o false, muestro el removeIcon o no.
showPlusIcon se lo paso nada más a los elementos de las otras listas, desde Home.jsx, y el valor es true si el item no está en myList y false en caso contrario, de esta manera:

<Categories title="Tendencias">
  <Carousel>
    {trends.map(item => 
      <CarouselItem
        key={item.id}
        {...item}
        showPlusIcon={!myList.some(myListItem => myListItem.id === item.id)}
      />
    )}
  </Carousel>
</Categories>

Y así queda la validación en CarouselItem:

{showPlusIcon &&
                        <img 
                            className="carousel-item__details--img"
                            src={plusIcon}
                            alt="Plus Icon"
                            onClick={handleSetFavorite}
                        />
                    }
                    {showRemoveIcon &&
                        <img 
                            className="carousel-item__details--img"
                            src={removeIcon}
                            alt="Remove Icon"
                            onClick={handleRemoveFavorite}
                        />
                    }

s

Super!

Práctica:

Para arreglar el bug, implemente el siguiente codigo
if (state.myList.filter((item) => payload.id === item.id).length > 0) {
return { …state };
}
return { …state, myList: […state.myList, payload] };