No tienes acceso a esta clase

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

Completando el StorageEventListener

15/19
Recursos

Aportes 21

Preguntas 7

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Regístrate o inicia sesión para participar.

Les doy la bienvenida a una nueva edición de…
🌈 Encuentra el error del profesor 🌈
Estrellita a la primera persona que lo resuelva. 😉

.

👉 ¿Cuál es el error con el código de esta clase?

  • Pista #1: está relacionado a rendimiento
  • Pista #2: está relacionado a los efectos

.

Pero ¿Juan, cuáles efectos?
Exacto, ¿cuáles efectos? Ese es el problema…

.

Respuestas aquí en los comentarios 👇

Aporte respecto al event listener "storage" (addEventListener(‘storage’)):

No funcionará en la misma página que realiza los cambios; en realidad, es una forma de que otras páginas del dominio usen el almacenamiento para sincronizar los cambios que se realicen. Las páginas de otros dominios no pueden acceder a los mismos objetos de almacenamiento.

Esto lo saqué de MDN
![](

Para que no aparezca los todos mientras están sincronizándose

{!props.loading && props.searchedTodos.map(renderFunc)}

Estuve lidiando mucho rato con un concepto que entendí mal. Ya que no me aparecía el mensaje de cambio, pero era porque solo estaba usando una ventana. El evento se dispara cuando se modifica el local storage en el contexto de otro documento. Les dejo enlace por si les pasa igual. En resumen, tienen que tener dos pestañas abiertas
Más información

Se crea css para mostrar el boton de refresh y se pasa a una ventana modal

Con respecto a como se soluciono el hecho que no se mostrara el listado y solo el skeleton loading queda asi mi solucion

import "./todoList.css";
const TodoList = ({
  children,
  error,
  loading,
  showEmptyTodos,
  showRender,
  showEmptySearchResults,
  onError,
  onLoading,
  onEmptyTodos,
  onEmptySearchResults,
  render
}) => {
  return (
    <section className="todolist-container">
      {error && onError()}
      {loading && onLoading()}
      {showEmptyTodos && onEmptyTodos()}
      {showEmptySearchResults && onEmptySearchResults()}
      {showRender && render()}
      <ul>{children}</ul>
    </section>
  );
};

export default TodoList;

Repo

Project TODOs

import React from 'react';
import { withStorageListener } from './withStorageListener';
// Se importa el HOC



//Componente
function Changealert({show,toggleShow,setItem}){

    if(show){  //Si show es verdadero
        return (

            <div>
                 <p>Hubo Cambios</p>
                 <button onClick={()=>{
                     
                     toggleShow(false);

                     const localStorageItem = localStorage.getItem('TODOS_V1');
                     const parsedItem = JSON.parse(localStorageItem);         
                     setItem(parsedItem)
                     
                     }}>Volver a cargar la información</button>
                
            </div>

        );

    }else{
            
        return null;

    }

       

}

const ChangeAlertWithStorageListener=withStorageListener(Changealert);
//Se crea una variable , y envuelve en el HOC componente Changealert


export {ChangeAlertWithStorageListener};
//Se exporta este vendria siendo el componente de verdad

Para recargar la página en onClick llame únicamente window.location.reload() y al recargar la ya no hacía falta toggleShow porque por defecto se volvía a false.

<button  onClick={() => {window.location.reload();}}>

No se que tan malooo sea hacer esto, pero toda la parte de la sincronización la veo más sencilla de hacerla asi simplemente:

function useLocalStorage(itemName, defaultValue = [], withSync = true) {

    const [sync, setSync] = React.useState(false)
    const [loading, setLoading] = React.useState(true);
    const [items, setItems] = React.useState(defaultValue);
    const [error, setError] = React.useState(false);

    if (withSync) {
        window.addEventListener("storage", () => {
            setSync(sync + 1);
        });
    }

Si esto genera problemas de optimización o se considera una mala practica, pues creo que ahi esta el detalle, pero si no para mi queda de lujo 😂

Así es como resolví los retos agregue una validación para no iterar los elementos al menos que no hubiera un error y no se encontrará cargando la App. Puse un botón para darle al usuario la posibilidad de no recargar la página.

antes de ver la solucion de eprofe lo que hice fue que el pedido de el contenido de local storage fuese una funcion tal que asi y cada que haya u cambio en storage invocar a update() y el cartel lo oculto en 3s para que no sea molesto y tambien se cierra con el botton

const update= ()=>{
        try{...
        }catch(err){...
        }
    }
React.useEffect(()=>{
    setTimeout(()=>{
        update();
        },1500);
    }, []);

Para mostrar los cambios al hacer click en el boton, lo que hice fue -> <button
onClick={() => window.location.reload(false)}
>
Volver a cargar la informacion
</button>
</div>

Mi solución a que no salgan los todos mientras está en loading fue añadir una condición extra antes de renderizar los todos, esa condición es !props.loading, para que si el valor de loading es true no se renderice nada y cuando sea false ya pueda renderizar 😄

Buenas, al desafío del profe Juan Davíd lo resolví de dos formas, una fácil y rápida pero que es muy muy básica y otra más pensada.
La primera simplemente agregue un window.location.reload() en la función del botón.
La segunda llevé el estado storageChange al hook useLocationStorage para que el useEffect corra cada vez que ese estado se actualice.
Sigo con el video!

Yo lo que hice para el reto de al final fue pasarle al TodoItem la propiedad loading

<TodoItem
                key={todo.text}
                text={todo.text}
                loading={loading}
                completed={todo.completed}
                onComplete={() => completeTodo(todo.text)}
                onDelete={() => deleteTodo(todo.text)}
/>

después en TodoItem checo si loading es true y si es true le agrego la clase TodoItem–loading

<li className={`TodoItem ${!!props.loading && 'TodoItem--loading'}` }>
      <CompleteIcon 
        completed={props.completed}
        onComplete={props.onComplete}
      />
      <p 
      className={`TodoItem-p ${props.completed && 'TodoItem-p--complete'}`}>
        {props.text}
      </p>
      <DeleteIcon
      onDelete={props.onDelete}
      />
    </li>

lo que hace esa clase es hacerle un display none a los items

 .TodoItem--loading{
    display: none;
  }

y tada!! ya no aparecen los todosmientras se sincronizan los todos

Para que no aparezcan mientras se actualiza, preferí afectar de una vez la función Render, en mi Render Props:

	render={todo => (
            (sincronizedTodos) ?
              <TodoItem 
                key={todo.text} 
                text={todo.text} 
                completed={todo.completed} 
                color={todo.color} 
                todoComplete={() => todoComplete(todo.text)} 
                todoDelete={() => todoDelete(todo.text)}
              />
              : null
          )

Solución a los retos:
Para que al cargar no mostrar los TODOs:

import './TodoList.css'

function TodoList(props) {
    return (
        <section className="TodoList-container">
            {props.error && props.onError()}
            {props.loading && props.onLoading()}
            {(!props.loading && !props.totalTodos) && props.onEmpty()}
            {(Boolean(props.totalTodos) && !props.searchedTodos.length) && props.onEmptySearchResults(props.searchText)}

            <ul>
                {!props.loading && props.searchedTodos.map(props.render || props.children)}
            </ul>
        </section>
    )
}

export { TodoList }

En cuanto a no dejar modificar hasta que se actualice:

Agregaria en useLocalStorage:

const [canChange, setCanChange ] = React.useState(true);

Y luego lo acomodo a los diferentes componentes:

function CreateTodoButton({setOpenModal,canChange}) {
    const onClickButton = () =>{
        setOpenModal(prevState=>!prevState);
    }

    return (
        <button 
        className="CreateTodoButton"
        onClick={onClickButton}
        disabled={!canChange}
        >+</button>
    )
}

export { CreateTodoButton }

Si les queda dudas me lo piden y agrego los detalles en los comentarios de este comentario jejeje

yo simplemente lo que hice fue algo sensillo coloque dentro del button una etiqueta a con href="." y esto automaticamente me recarga la pagina

function ChangeAlert({show, toggleShow}) {
  if (show) {
    return (
      <div>
        <p>Hubo Cambios</p>
        <button
          onClick={() => toggleShow(false)}
        >
        <a href=".">Volver a cargar</a>
        </button>
      </div>
    );
  } else {
    return null;
  }
}

Lo que yo hice para solucionar el reto fue exportar la variable “sincronizedItem” del hook useLocalStorage, la importe en el hook useTodos y la volvi a exportar en este, luego la importe en el componente App y se la envíe como props a mi componente TodoList, en el componente TodoList solo valide lo siguiente.

  {
        props.sincronizing && props.searchedTodos.map( todo => renderFunc(todo))
      }

El reto es bastante sencillo… bueno para mí qué tengo experiencia animando cosas con css y js. El único problema está en que firefox no permite hacer un drop image en este contendor 😦 no lo podrán ver.

Lo único que hice es agregar condiciones que involucren clases para ciertos elementos que serán afectados con los estados de la página

Como pueden ver aquí, solo agregué como dependencia del elemento él prop.loading

Aquí se ve el uso que le doy, si la página está cargando, agrego una opacidad en los items y posicione un pseudo-elemento para evitar el uso de los items

<li className={`TodoItem ${props.loading? 'TodoItem-Loading' : ''}`}>

Sin necesidad de modificar el código que tenemos y sin cambios bruscos al momento de carga la nueva información

Quizás cuando termine el curso deje el repositorio para que lo vean

Mmm me gustó esta clase, pero creo que se sobrecomplicó algo que en realidad es muy simple.
.
Entiendo que es con fines educativos, pero resulta un poco mareante.
.
En lugar de usar HOCs, tranquilamente podríamos mostrar el alert con:

{ sincronized && <AlertWithStorageListener /> }

Esto funciona, pero me queda la duda, es esto correcto y la complejización con HOCs que se hace en la clase es meramente con fines educativos, o tiene razón de ser el hecho de haber hecho este HOC?

He tenido problemas con el sincronizeTodos por inercia de mi parte he colocado sincronizedTodos en mi caso me hubiera gustado que la palabra fuera otra mas diferencial alguna sugerencia ?