Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

148
Views
Evite volver a renderizar todos los componentes de la lista mientras actualiza solo uno en React

Tengo una aplicación de chat simple que usa Firebase v9, con estos componentes de padre a hijo en este orden jerárquico: ChatSection , Chat , ChatLine , EditMessage .

Tengo un gancho personalizado llamado useChatService que contiene la lista de messages en estado, el gancho se llama en ChatSection , el gancho devuelve los messages y los paso de ChatSection en un accesorio a Chat , luego recorro los messages y creo un componente ChatLine para cada mensaje

Puedo hacer clic en el botón Edit frente a cada mensaje, muestra el componente EditMessage para que pueda editar el texto, luego, cuando presiono "Enter", la función updateMessage se ejecuta y actualiza el mensaje en la base de datos, pero luego cada ChatLine se vuelve a renderizar, lo cual es un problema a medida que la lista crece.

EDICIÓN 2: Completé el código para hacer un ejemplo de trabajo con Firebase v9 para que pueda visualizar las reproducciones de las que estoy hablando después de cada (agregar, editar o eliminar) de un mensaje. Estoy usando ReactDevTools Profiler para realizar un seguimiento de los renderizados.

  • Aquí está el código actualizado completo: CodeSandbox
  • También implementado en: Netlify

ChatSection.js :

 import useChatService from "../hooks/useChatService"; import { useEffect } from "react"; import Chat from "./Chat"; import NoChat from "./NoChat"; import ChatInput from "./ChatInput"; const ChatSection = () => { let unsubscribe; const { getChatAndUnsub, messages } = useChatService(); useEffect(() => { const getChat = async () => { unsubscribe = await getChatAndUnsub(); }; getChat(); return () => { unsubscribe?.(); }; }, []); return ( <div> {messages.length ? <Chat messages={messages} /> : <NoChat />} <p>ADD A MESSAGE</p> <ChatInput /> </div> ); }; export default ChatSection;

Chat.js :

 import { useState } from "react"; import ChatLine from "./ChatLine"; import useChatService from "../hooks/useChatService"; const Chat = ({ messages }) => { const [editValue, setEditValue] = useState(""); const [editingId, setEditingId] = useState(null); const { updateMessage, deleteMessage } = useChatService(); return ( <div> <p>MESSAGES :</p> {messages.map((line) => ( <ChatLine key={line.id} line={line} editValue={line.id === editingId ? editValue : ""} setEditValue={setEditValue} editingId={line.id === editingId ? editingId : null} setEditingId={setEditingId} updateMessage={updateMessage} deleteMessage={deleteMessage} /> ))} </div> ); }; export default Chat;

ChatInput :

 import { useState } from "react"; import useChatService from "../hooks/useChatService"; const ChatInput = () => { const [inputValue, setInputValue] = useState(""); const { addMessage } = useChatService(); return ( <textarea onKeyPress={(e) => { if (e.key === "Enter") { e.preventDefault(); addMessage(inputValue); setInputValue(""); } }} placeholder="new message..." onChange={(e) => { setInputValue(e.target.value); }} value={inputValue} autoFocus /> ); }; export default ChatInput;

ChatLine.js :

 import EditMessage from "./EditMessage"; import { memo } from "react"; const ChatLine = ({ line, editValue, setEditValue, editingId, setEditingId, updateMessage, deleteMessage, }) => { return ( <div> {editingId !== line.id ? ( <> <span style={{ marginRight: "20px" }}>{line.id}: </span> <span style={{ marginRight: "20px" }}>[{line.displayName}]</span> <span style={{ marginRight: "20px" }}>{line.message}</span> <button onClick={() => { setEditingId(line.id); setEditValue(line.message); }} > EDIT </button> <button onClick={() => { deleteMessage(line.id); }} > DELETE </button> </> ) : ( <EditMessage editValue={editValue} setEditValue={setEditValue} setEditingId={setEditingId} editingId={editingId} updateMessage={updateMessage} /> )} </div> ); }; export default memo(ChatLine);

EditMessage.js :

 import { memo } from "react"; const EditMessage = ({ editValue, setEditValue, editingId, setEditingId, updateMessage, }) => { return ( <div> <textarea onKeyPress={(e) => { if (e.key === "Enter") { // prevent textarea default behaviour (line break on Enter) e.preventDefault(); // updating message in DB updateMessage(editValue, setEditValue, editingId, setEditingId); } }} onChange={(e) => setEditValue(e.target.value)} value={editValue} autoFocus /> <button onClick={() => { setEditingId(null); setEditValue(null); }} > CANCEL </button> </div> ); }; export default memo(EditMessage);

useChatService.js :

 import { useCallback, useState } from "react"; import { collection, onSnapshot, orderBy, query, serverTimestamp, updateDoc, doc, addDoc, deleteDoc, } from "firebase/firestore"; import { db } from "../firebase/firebase-config"; const useChatService = () => { const [messages, setMessages] = useState([]); /** * Get Messages * * @returns {Promise<Unsubscribe>} */ const getChatAndUnsub = async () => { const q = query(collection(db, "messages"), orderBy("createdAt")); const unsubscribe = onSnapshot(q, (snapshot) => { const data = snapshot.docs.map((doc, index) => { const entry = doc.data(); return { id: doc.id, message: entry.message, createdAt: entry.createdAt, updatedAt: entry.updatedAt, uid: entry.uid, displayName: entry.displayName, photoURL: entry.photoURL, }; }); setMessages(data); }); return unsubscribe; }; /** * Memoized using useCallback */ const updateMessage = useCallback( async (editValue, setEditValue, editingId, setEditingId) => { const message = editValue; const id = editingId; // resetting state as soon as we press Enter setEditValue(""); setEditingId(null); try { await updateDoc(doc(db, "messages", id), { message, updatedAt: serverTimestamp(), }); } catch (err) { console.log(err); } }, [] ); const addMessage = async (inputValue) => { if (!inputValue) { return; } const message = inputValue; const messageData = { // hardcoded photoURL, uid, and displayName for demo purposes photoURL: "https://lh3.googleusercontent.com/a/AATXAJwNw_ECd4OhqV0bwAb7l4UqtPYeSrRMpVB7ayxY=s96-c", uid: keyGen(), message, displayName: "John Doe", createdAt: serverTimestamp(), updatedAt: null, }; try { await addDoc(collection(db, "messages"), messageData); } catch (e) { console.log(e); } }; /** * Memoized using useCallback */ const deleteMessage = useCallback(async (idToDelete) => { if (!idToDelete) { return; } try { await deleteDoc(doc(db, "messages", idToDelete)); } catch (err) { console.log(err); } }, []); const keyGen = () => { const s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; return Array(20) .join() .split(",") .map(function () { return s.charAt(Math.floor(Math.random() * s.length)); }) .join(""); }; return { messages, getChatAndUnsub, updateMessage, addMessage, deleteMessage, }; }; export default useChatService;

Cuando un mensaje se actualiza usando el método updateMessage , solo necesito que se ChatLine a procesar la línea de chat afectada (lo mismo para agregar y eliminar), no todas las líneas de ChatLine en la lista, mientras mantengo el estado de los messages pasados de ChatSection a Chat , entiendo que ChatSection & Chat debería volver a mostrarse, pero no todas las ChatLine de la lista. (También se ChatLine )

EDIT 1: Supongo que el problema es con setMessages(data) en useChatService.js , pero pensé que React solo volvería a mostrar la línea editada porque ya proporcioné la key={line.id} al recorrer los messages en el componente Chat , pero yo no tengo idea de cómo arreglar esto.

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

Esto es lo que creo: está pasando Messages en ChatSection y eso significa que cuando los Messages se actualicen, ChatSection se volverá a procesar y todos sus elementos secundarios también se volverán a procesar.

Así que aquí está mi idea de eliminar Messages de ChatSection y solo agregarlo en Chat .

Ya está usando useChatService en Chat, por lo que agregar Messages allí debería ser mejor.

Pruebe esto y nos devuelva también si funciona.

Si todavía no es como le gustaría que fuera, también hay otra manera de arreglarlo.

Pero debe crear un ejemplo de trabajo para nosotros para que podamos echar un vistazo y hacer pequeños cambios.

over 4 years ago · Santiago Trujillo Report

0

Envuelva ChatLine dentro de React.memo, detendrá múltiples rereders.

NOTA: Actualice la función areEqual según su caso de uso.

 import { useState } from "react"; import ChatLine from "./ChatLine"; import useChatService from "../hooks/useChatService"; function areEqual(prevProps, nextProps) { /* return true if passing nextProps to render would return the same result as passing prevProps to render, otherwise return false */ return prevProps.line === nextProps.line; } const ChatLineMemo = React.memo(ChatLine, areEqual); const Chat = ({ messages }) => { const [editValue, setEditValue] = useState(""); const [editingId, setEditingId] = useState(null); const { updateMessage, deleteMessage } = useChatService(); return ( <div> <p>MESSAGES :</p> {messages.map((line) => ( <ChatLineMemo key={line.id} line={line} editValue={line.id === editingId ? editValue : ""} setEditValue={setEditValue} editingId={line.id === editingId ? editingId : null} setEditingId={setEditingId} updateMessage={updateMessage} deleteMessage={deleteMessage} /> ))} </div> ); }; export default Chat;
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!