Quiero cambiar el contenido de un componente secundario en respuesta a un evento de usuario en otro componente secundario sin que el elemento principal se vuelva a procesar.
Intenté almacenar el setter de estado secundario en una variable en el elemento principal, pero no está definido en el momento en que todos los elementos secundarios se han renderizado, por lo que no funciona.
¿Hay una forma mínima de lograr esto (sin instalar una biblioteca de administración de estado)?
NiñoANiño.tsx
import React, { Dispatch, SetStateAction, useEffect, useRef, useState, } from "react"; export default function ChildToChild() { const renderCounter = useRef(0); renderCounter.current = renderCounter.current + 1; let setChildOneContent; const childOneContentController = ( setter: Dispatch<SetStateAction<string>> ) => { setChildOneContent = setter; }; return ( <div> <h1>Don't re-render me please</h1> <p>No. Renders: {renderCounter.current}</p> <ChildOne childOneContentController={childOneContentController} /> <ChildTwo setChildOneContent={setChildOneContent} /> </div> ); } function ChildOne({ childOneContentController, }: { childOneContentController: (setter: Dispatch<SetStateAction<string>>) => void; }) { const [content, setContent] = useState("original content"); useEffect(() => { childOneContentController(setContent); }, [childOneContentController, setContent]); return ( <div> <h2>Child One</h2> <p>{content}</p> </div> ); } function ChildTwo({ setChildOneContent, }: { setChildOneContent: Dispatch<SetStateAction<string>> | undefined; }) { return ( <div> <h2>Child Two</h2> <button onClick={() => { if (setChildOneContent) setChildOneContent("content changed"); }} > Change Child One Content </button> </div> ); }Deberías ser capaz de lograr por
setState del componente secundario. Esto desencadenará una nueva representación del niño pero no de sí mismo (padre).Demostración: https://codesandbox.io/s/child-re-rendering-only-x37ol
Para una sola vez, puede usar lo siguiente (otra ref para almacenar y usar ChildOne setContent :
import React, { Dispatch, SetStateAction, useEffect, useRef, useState } from "react"; function ChildOne({ setChildOneRef }: { setChildOneRef: React.MutableRefObject<React.Dispatch< React.SetStateAction<string> > | null>; }) { const [content, setContent] = useState("original content"); useEffect(() => { setChildOneRef.current = setContent; }, [setChildOneRef]); return ( <div> <h2>Child One</h2> <p>{content}</p> </div> ); } function ChildTwo({ setChildOneRef }: { setChildOneRef: React.MutableRefObject<React.Dispatch< React.SetStateAction<string> > | null>; }) { return ( <div> <h2>Child Two</h2> <button onClick={() => { setChildOneRef.current?.("content changed"); }} > Change Child One Content </button> </div> ); } export function ChildToChild() { const renderCounter = useRef(0); renderCounter.current = renderCounter.current + 1; const setChildOneRef = useRef<Dispatch<SetStateAction<string>> | null>(null); return ( <div> <h1>Don't re-render me please</h1> <p>No. Renders: {renderCounter.current}</p> <ChildOne setChildOneRef={setChildOneRef} /> <ChildTwo setChildOneRef={setChildOneRef} /> </div> ); }Si este patrón es común en su código, es posible que aún desee usar la biblioteca de administración de estado o evaluar si los "hijos" deben estar realmente separados.
Puede crear un objeto, almacenar el gancho en ese objeto y usar el gancho del objeto en los niños. No estoy seguro de por qué el recuento de salida se incrementa dos veces ...
import { useState } from "react"; let parentRenderCount = 0; let child1RenderCount = 0; let child2RenderCount = 0; const Child1 = ( { stateHolder } ) => { const [count, setCount] = useState( 0 ) console.log('child1 render', ++child1RenderCount) stateHolder.count = count stateHolder.setCount = setCount return <div> {count} </div> } const Child2 = ( { stateHolder } ) => { console.log('child2 render', ++child2RenderCount) return <button onClick={() => stateHolder.setCount( stateHolder.count + 1 )}> clickme </button> } function App() { const stateHolder = { } console.log('parent render', ++parentRenderCount) return ( <div className="App"> <Child1 stateHolder={stateHolder} /> <Child2 stateHolder={stateHolder} /> </div> ); } export default App;