Aquí está mi ejemplo:
const App = () => { const [elements, setElements] = useState<{ text: string; onClick: () => any }[]>([]) useEffect(() => setElements([ { text: 'Click me', onClick: handleClick, } ]), []); const handleClick = () => { // I want to do something with elements here console.log(elements); // logs [] } // Currently updated elements console.log(elements); return ( <> {elements.map((e, i) => ( <button key={i} onClick={e.onClick}> {e.text} </button> )) } </> ) };Marqué la parte problemática del código con un comentario. ¿Alguien puede explicar por qué esto no funciona? Gracias por adelantado.
La función handleClick de su componente tendrá muchas funciones handleClick declaradas dentro de ella a lo largo de su "vida útil". Cada vez que su componente se vuelve a renderizar (y en el montaje inicial), está redefiniendo su función handleClick. Esto se debe a que cada renderizado hace que el cuerpo de su componente se ejecute nuevamente. Cada vez que se define su función handleClick , "recuerda" las variables definidas dentro de su alcance circundante en el momento en que se definió, como la matriz de elements . Esta idea de dónde una función puede acceder a las variables definidas dentro de su alcance en el que se definió se llama cierre .
En su código, su useEffect está guardando la "primera versión" de su función handleClick que se creó en el montaje inicial de su componente. En ese momento, el estado de sus elements estaba vacío y, por lo tanto, esa versión de su función handeClick usará la versión vacía de la matriz de elements en las representaciones posteriores. Esto se debe a que, en renderizaciones futuras cuando los elements tienen valores, su objeto dentro de los elements tendrá una función handleClick que se refiere a la función handleClick original (que solo conoce la matriz de elements vacíos).
No estoy seguro si recomendaría almacenar funciones en el estado, ya que puede generar problemas como los que está teniendo. En su lugar, sugeriría eliminar la propiedad onClick de sus objetos y pasar su elemento e de la devolución de llamada .map() a handleClick (es decir, si necesita usar e específicamente). Si hace esto, entonces handleClick será el mismo definido para el render actual en el que se encuentra, lo que le permitirá acceder a los elements correctamente. O si realmente lo desea, puede pasar elements y no confiar en el valor del ámbito circundante en el que se definió (sin embargo, dependiendo de su uso real para esto, podría haber una forma más reactiva de hacerlo) :
const {useEffect, useState} = React; const App = () => { const [elements, setElements] = useState([]); useEffect(() => setElements([ { text: 'Click me', onClick: handleClick, } ]), []); const handleClick = (elements) => { console.log(elements); } // Currently updated elements console.log(elements); return ( <React.Fragment> {elements.map((e, i) => ( <button key={i} onClick={() => e.onClick(elements)}> {e.text} </button> )) } </React.Fragment> ) }; ReactDOM.render(<App />, document.body); <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>