Estoy tratando de alternar la visibilidad de un componente usando otro componente como este:
const CompA = React.memo(({Vis})=>{ console.log('Rendered CompA'); return(<Pressable onPress={()=>{Vis[1](!Vis[0])}} style={...}></Pressable>); }) const CompB = React.memo(({Vis})=>{ console.log('Rendered CompB'); return(<>{Vis[0]&&(<View style={...}></View>)}</>); }) export default function App() { const Vis=useState(()=>true); return (<View style={styles.body}> <CompA Vis={Vis}/> <CompB Vis={Vis} /> </View>); } pero cada vez que cambio la visibilidad, incluso CompA se renderiza
¿Cómo vuelvo a renderizar CompB cada vez que cambio la visibilidad?
La matriz que recibe de useState nunca será === una matriz anterior que recibió de useState ; React crea una nueva matriz cada vez que llamas a useState . Al igual que [] === [] siempre es falso, la verificación realizada por memo en los accesorios siempre será falsa y se volverá a procesar.
Hay al menos dos formas de resolver el problema:
Pase las partes componentes de esa matriz en su lugar, ya que se garantiza que la función setter es estable, y solo pase CompA la función setter ya que no necesita la bandera (puede usar el formulario de devolución de llamada del setter).
Si realmente desea pasar la matriz, implemente una devolución de llamada de memo personalizada para CompA que solo mire el segundo elemento de la matriz, y no use el primer elemento de la matriz en CompA . Pero recomiendo enfáticamente no hacer eso, la legibilidad/semántica es muy engañosa/sorprendente.
Aquí está el #1:
const CompA = React.memo(({setVisible}) => { console.log("Rendered CompA"); return (<Pressable onPress={() => {setVisible(visible => !visible)}} style={...}></Pressable>); }); const CompB = React.memo(({visible}) => { console.log("Rendered CompB"); return (<>{visible && (<View style={...}></View>)}</>); }); export default function App() { const [visible, setVisible] = useState(true); // No need for the callback form here return (<View style={styles.body}> <CompA setVisible={setVisible} /> <CompB visible={visible} /> </View>); }Ejemplo en vivo:
const {useState} = React; const CompA = React.memo(({setVisible}) => { console.log("Rendered CompA"); return (<button onClick={() => {setVisible(visible => !visible)}}>pressable</button>); }); const CompB = React.memo(({visible}) => { console.log("Rendered CompB"); // Unfortunately, the version of Babel used by Stack // Snippets is so old it doesn't understand shorthand // fragment syntax return (<React.Fragment>{visible && (<div>this is the View</div>)}</React.Fragment>); }); function App() { const [visible, setVisible] = useState(true); // No need for the callback form here return (<div> <CompA setVisible={setVisible} /> <CompB visible={visible} /> </div>); } ReactDOM.render(<App />, document.getElementById("root")); <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>Debe pasar accesorios particulares en componentes. isVisible y la función que cambia el valor de isVisible . Al pasar function como accesorios p a los componentes y usar React.memo , debe usar useCallback en los ganchos de function para evitar volver a renderizar cosas.
Leer más sobre memo y usecallback
import React from "react"; import ReactDOM from "react-dom"; const { useState, useCallback, memo } = React; const CompA = memo(({ handleChange }) => { console.log("Rendered CompA"); return <Pressable onPress={handleChange}>Comp A</Pressable>; }); const CompB = memo(({ isVisible }) => { console.log("Rendered CompB", isVisible); return isVisible && <View> CompB</View>; }); function App() { const [isVisible, setVisible] = useState(true); const handleChange = useCallback(() => setVisible((isVisible) => !isVisible),[]); return ( <View style={styles.body}> <CompA handleChange={handleChange} /> <CompB isVisible={isVisible} /> </View> ); } ReactDOM.render(<App />, document.getElementById("root"));Ejemplo en vivo
const { useState, useCallback, memo } = React; const CompA = memo(({ handleChange }) => { console.log("Rendered CompA"); return <button onClick={handleChange}>Comp A</button>; }); const CompB = memo(({ isVisible }) => { console.log("Rendered CompB"); return isVisible && <div> CompB</div>; }); function App() { const [isVisible, setVisible] = useState(true); const handleChange = useCallback( () => setVisible((isVisible) => !isVisible), [] ); return ( <div> <CompA handleChange={handleChange} /> <CompB isVisible={isVisible} /> </div> ); } ReactDOM.render(<App />, document.getElementById("root")); <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script> <div id="root"></div>Tengo una solución pero no se ve bonita:
let UniVis; const CompA = React.memo(({Vis})=>{ console.log('Rendered CompA'); return(<Pressable onPress={()=>{UniVis[1](!UniVis[0])}} style={...}></Pressable>); }); const CompB = React.memo(({Vis})=>{ console.log('Rendered CompB'); return(<>{Vis[0]&&(<View style={...}></View>)}</>); }) export default function App() { const Vis=useState(()=>true); UniVis=Vis; return (<View style={styles.body}> <CompA /> <CompB Vis={Vis} /> </View>); }pero funciona