No tienes acceso a esta clase

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

Aprovecha el precio especial y haz tu profesión a prueba de IA

Antes: $249

Currency
$209
Suscríbete

Termina en:

0 Días
6 Hrs
53 Min
4 Seg

Notificando cambios con StorageEventListener

14/19
Recursos

¿Cómo aprovechar los high-order components en React para detectar cambios de estado?

Te has preguntado alguna vez cómo facilitar la sincronización de datos en aplicaciones React que se ejecutan en varias pestañas del navegador? Descubrirás a través de los high-order components y los eventos JavaScript cómo optimizar el rendimiento y evitar renderizaciones innecesarias. Esta guía te llevará por el proceso de implementación paso a paso, asegurando que comprendas y apliques estos conceptos a tus propios proyectos de React.

¿Qué hacer con los errores de sincronización de datos en múltiples pestañas?

Al trabajar con aplicaciones de React que acceden al Local Storage, es común enfrentar problemas de sincronización de datos cuando actualizaciones se realizan en diferentes pestañas del navegador. Esto se debe a que, por defecto, los cambios no se propagan automáticamente entre las sesiones. Aunque el uso de setTimeout para detectar cambios cada cierto tiempo puede parecer una solución, resulta ineficiente a largo plazo y afecta el rendimiento de la aplicación.

¿Cómo afecta el rendimiento el uso ineficiente de efectos en React?

A menudo usamos efectos en React para manejar actualizaciones de estado, pero olvidamos optimizarlos. La clave está en controlar cuándo deben ejecutarse los efectos. El uso incorrecto de efectos, como llamar repetidamente un efecto al no especificar adecuadamente las dependencias, puede causar múltiples renderizaciones no deseadas. Para evitarlo, podemos pasar un array vacío [] como segundo parámetro en useEffect, asegurando así que el efecto solo se ejecute al montar el componente.

¿Cómo utilizar los eventos del navegador para detectar cambios de datos?

Para manejar de manera efectiva la sincronización entre pestañas, podemos valernos del evento storage. Este evento se dispara en los documentos cuando el almacenamiento local detecta que un cambio ha sido realizado por otra pestaña. Implementa un EventListener para este evento, que te permitirá recibir información sobre cambios ocurridos, incluidos el elemento modificado y su nuevo valor.

window.addEventListener('storage', (event) => {
  console.log(event);
});

¿Cómo crear el componente Change Alert?

El primer paso es construir un componente que nos alerte sobre cambios en el almacenamiento local. Además, crearemos un high-order component (HOC) que integra este listenener y controla cuándo el componente debería mostrar la alerta.

  1. Definir el componente Change Alert:
import React from 'react';

function ChangeAlert() {
  return null;
}

export default ChangeAlert;
  1. Crear el HOC withStorageListener:
import React, { useState } from 'react';

function withStorageListener(WrappedComponent) {
  return function WrappedComponentWithStorageListener(props) {
    const [storageChange, setStorageChange] = useState(false);

    window.addEventListener('storage', () => {
      setStorageChange(true);
    });

    return <WrappedComponent show={storageChange} toggleShow={setStorageChange} {...props} />;
  };
}

¿Cómo integrar el high-order component en la aplicación?

Ahora que tenemos nuestro HOC, es momento de integrarlo al componente Change Alert y comprobar cómo el evento de almacenamiento notifica sobre cambios.

  1. Combinar el componente Change Alert con el HOC:
import ChangeAlert from './ChangeAlert';
import withStorageListener from './withStorageListener';

const ChangeAlertWithStorageListener = withStorageListener(ChangeAlert);
export default ChangeAlertWithStorageListener;
  1. Usar el componente en App.js:
import ChangeAlertWithStorageListener from './ChangeAlertWithStorageListener';

// ...

function App() {
  return (
    <div>
      {/* ...otros componentes */}
      <ChangeAlertWithStorageListener />
    </div>
  );
}

Con estos pasos, no solo optimizas el rendimiento de tus aplicaciones React, sino que también introduces un mecanismo robusto para mantener la coherencia de datos entre múltiples pestañas del navegador. ¡Adelante y sigue experimentando con high-order components para llevar tus proyectos al siguiente nivel!

Aportes 17

Preguntas 5

Ordenar por:

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

🤔 Pero ¿profe Juan, por qué haces como que no ves la advertencia de missing dependencias en la consola?
.
🌈 Los efectos son un tema espectacularmente divertidísimo dentro de React. Una de sus particularidades consideradas entre las más avanzados son las actualizaciones al estado dentro de los efectos y cómo estas afectan a los renders de nuestros componentes.
.
Si quieres que estudiemos este tema con la profundidad que merece, responde este comentario con un enorme 💚 y pondremos todo nuestro esfuerzo en grabar un Curso de React.js: Optimización de Render y Debugging.

Y recuerden amigos, si quieren usar TypeScript para completar este curso, deben de usar la extension correcta en sus archivos. Tuve un error raro que no entendia y demore una hora en encontrarlo. Al final era que el archivo debia ser .tsx en lugar de .ts.

Hace unas cuantas lecciones preguntaba qué se podía hacer para evitar esa recarga, que ya había evidencia con Console.log. Ahora ya sé por qué! Jajajaja 🤣
Estoy feliz porque ya puedo dormir, no me sacaba la idea de la cabeza de que algo andaba mal con mi aplicación 😁

jaja no entendí nada, este curso está dificil 😦

ChangeAlert/withStorageListener.js

import React from "react";

function withStorageListener(WrappedComponent) {
    return function WrappedComponentWithStorageListener(props) {
        const [storageListener, setStorageListener] = React.useState(false);
        return (
            <WrappedComponent
                show={storageListener}
                toggleShow={setStorageListener}
            />
        );
    }
}
export { withStorageListener };

ChangeAlert/index.js

import React from "react";
import { withStorageListener } from "./withStorageListener";

function ChangeAlert({show,toggleShow}) {
  if (show) {
    return <p>Hay Cambios!!!</p>;
  }else{

    return <p> No Hay Cambios!!!</p>;
  }

}
const ChangeAlertWithStorageListener = withStorageListener(ChangeAlert);
export { ChangeAlertWithStorageListener };

ChangeAlert/withStorageListener.js

import React, { useState } from 'react'

export const withStorageListener = (WrappedComponent) => {
    return function WrapedComponentWithStorageListener() {
        const [storageChange, setStorageChange] = useState(false)
        return (
                <WrappedComponent 
                    show={storageChange}
                    toggleShow={setStorageChange}
                />               
        )
    }
}

ChangeAlert/index.js

import React from 'react’
import { withStorageListener } from ‘./withStorageListener’

const ChangeAlert = ({show, toggleShow}) => show && <p>hubo cambios!</p> || null

const ChangeAlertWithStorageListener = withStorageListener(ChangeAlert)

export {ChangeAlertWithStorageListener};
ChangeAlert/index.js

Esta clase estuvo fuerte xd

Yo quiero ese curso de optimización de apps en React Uwu

Por el momento va de esta forma, el retorno lo deje con fragment vacío, igual podría ser null o con algún mensaje. ya que se debe si o si retornar algo para evitar que se rompa.
ChangeAlert/index.js

import React from 'react';
import { WithStorageListener } from './withStorageListener';

const ChangeAlert=({show,toggleShow})=>{
    if(show){return <p>Hubo cambios</p>}
    else{return <></>}
}

const ChangeAlertWithStorageListener = WithStorageListener(ChangeAlert)
export {ChangeAlertWithStorageListener}

ChangeAlert/WithStorageListener.js

import React from 'react';

const WithStorageListener=(WrappedComponent)=>
{
return function WrappComponentWithStorageListener(props){
 const [storageChange,setStorageChange]= React.useState(false)
    return (
    <WrappedComponent
    show={storageChange}
    toggleShow={setStorageChange}
    />
    )
}
}

export {WithStorageListener}

Codigo de
ChangeAlert/withStorageListener.js
me tome la libertad de agregar un else, o si no el codigo colapsa, haz al prueba.

import React from "react";
import { withStorageListener } from "./withStorageListener";

function ChangeAlert({ show, toggleShow }) {
    if(show){
        return <p>Habra cambios</p>;
    } else{
        return null;
    }
}

const ChangeAlertWithStorageListener = withStorageListener(ChangeAlert);

export { ChangeAlertWithStorageListener }

ChangeAlert/index.js

import React from "react";
import { withStorageListener } from "./withStorageListener";

function ChangeAlert({ show, toggleShow }) {
    if(show){
        return <p>Habra cambios</p>;
    } else{
        return null;
    }
}

const ChangeAlertWithStorageListener = withStorageListener(ChangeAlert);

export { ChangeAlertWithStorageListener }

Hola comunidad platzi, les dejo el codigo de la clase en TypeScript (lo cual estuvo bastante retador):

import { FC, useState } from "react"

function withStorageListener<T>(
    WrappedComponent: FC<T>
) {
    return function WrappedComponentWithStorageListener(props:Omit<T, "show" | "toggleShow">){
        
        const [storageChange, setStorageChange] = useState(false);

        return (
            <WrappedComponent 
                {...(props as T)}
                show={storageChange}
                toggleShow={setStorageChange}          
            />
        )
    }   
}

export { withStorageListener }
import { Dispatch, SetStateAction, FC } from 'react'
import { withStorageListener } from "./withStorageListener"

interface Props {
    show: boolean,
    toggleShow: Dispatch<SetStateAction<boolean>>,
}

const ChangeAlert:FC<Props> = ({
    show,
})=> {
    if(show){
        return(
            <p>¿Hubo cambios?</p>
        )
    } else {
        return null
    }
}

const ChangeAlertWithStorageListener = withStorageListener(ChangeAlert);

export { ChangeAlertWithStorageListener }
import { FC } from 'react'

import { TodoButton } from "../TodoButton"
import { TodoCounter } from "../TodoCounter"
import { TodoItem } from "../TodoItem"
import { TodoList } from "../TodoList"
import { TodoSearch } from "../TodoSearch"
import { TodosError } from "../TodoError"
import { EmptyTodos } from "../EmptyTodo"
import { TodosLoading } from "../TodoLoading"
import { Modal } from "../Modal"
import { TodoForm } from "../TodoForm"
import { TodoHeader } from "../TodoHeader"
import { useTodos } from './useTodos'
import { ChangeAlertWithStorageListener } from '../ChangeAlert'

const App: FC = () => {

  const {
    loading,
    error,
    searchedTodos,
    addTodo,
    completeTodo,
    deleteTodo,
    openModal,
    setOpenModal,
    completedTodos,
    totalTodos,
    searchValue,
    setSearchValue,
  } = useTodos();

  return (
    <>
      <TodoHeader
        loading={loading}
      >
        <TodoCounter
          completedTodos={completedTodos}
          totalTodos={totalTodos}
          // loading={loading}
        />
        <TodoSearch
          searchValue={searchValue}
          setSearchValue={setSearchValue}
          // loading={loading}
        />
      </TodoHeader>
      <TodoList
        loading={loading}
        error={error}
        onLoading={()=>(
          <>
            <TodosLoading />
            <TodosLoading />
            <TodosLoading />
          </>
        )}
        onError={()=>(
          <TodosError />
        )}
        onEmpty={()=><EmptyTodos />}
        searchedTodos={searchedTodos}
        searchText={searchValue}
        totalTodos={totalTodos}
        onEmptySearchResults={
          (searchText)=>(<p>No hay resultado para {searchText}</p>)
        }
        // render={(todo) => (
        //   <TodoItem
        //     key={todo.text}
        //     text={todo.text}
        //     completed={todo.completed}
        //     onComplete={() => { completeTodo(todo.text) }}
        //     onDelete={() => { deleteTodo(todo.text) }}
        //   />
        // )}
      >
        {
          (todo) => (
            <TodoItem
              key={todo.text}
              text={todo.text}
              completed={todo.completed}
              onComplete={() => { completeTodo(todo.text) }}
              onDelete={() => { deleteTodo(todo.text) }}
            />
          )
        }
      </TodoList>
      <TodoButton setOpenModal={setOpenModal} />

      {
        openModal && (
          <Modal>
            <TodoForm
              addTodo={addTodo}
              setOpenModal={setOpenModal}
            />
          </Modal>
        )
      }

      <ChangeAlertWithStorageListener />
    </>
  )
}



export default App

Las variables en ingles hacen mas difícil este clase

Tenia esta duda hace un tiempo, de que pasaria si se hacen cambios al mismo storage por ejemplo.
Con este curso estoy sacando algunas dudas que tenia, como cuando pensaba una solucion mas sencilla o alternativa a context como vimos composicion de componentes y colocacion de estado.

Notificando cambios con StorageEventListener

.
Si ejecutamos la aplicación en 2 ventanas nos daremos cuenta que al modificar un TODO; es decir eliminarlo, crearlo o marcarlo como completado, los cambios no se sincronizan y en una ventana van a aparecer después afectando a la consistencia de la aplicación.
.
Lo que se hace es crear un componente para ChangeAlert en su correspondiente carpeta.
.

// ChangeAlert/index.js

import React from 'react';
import { withStorageListener } from './withStorageListener';

function ChangeAlert({ show, toggleShow }) {
  if (show) {
    return <p>Hubo cambios</p>;
  }
}

const ChangeAlertWithStorageListener = withStorageListener(ChangeAlert);

export { ChangeAlertWithStorageListener };
// ChangeAlert/withStorageListener.js
import React from 'react';

function withStorageListener(WrappedComponent) {
  return function WrappedComponentWithStorageListener(props) {
    const [storageChange, setStorageChange] = React.useState(false);
    
    return (
      <WrappedComponent
        show={storageChange}
        toggleShow={setStorageChange}
      />
    );
  }
}

.
Este componente como podemos observar tiene a las propiedades show y toogleShow que se recibirán después de enviar el componente como parámetro HOC withStorageListener.
.
El HOC lo que hace es recibir un WrappedComponent luego renderiza un componente WrappedComponentWithStorageListener que internamente tiene un estado para saber si hubo cambios en el localStorage storageChange (inicialmente en false) y su correspondiente modificador setStorageChange, se retorna entonces un WrappedComponent con el estado inyectado en dicho componente.
.
Llamando al HOC de esa manera lo guardamos en la constante ChangeAlertWithStorageListener y es a este a quién exportamos para que la aplicación lo pueda renderizar.
.

function App() {
	...
	return (
    <React.Fragment>
			...
			<ChangeAlertWithStorageListener />
    </React.Fragment>
  );
}

export default App;

seria interesante el curso de optimización con react,.

Potente la clase profe 😵