Obtengo de una API donde quiero pasar el objeto de respuesta como accesorio a un componente secundario de App.js y usarlo en mi archivo Tags.js. Sin embargo, solo funciona después de una vez y cuando lo actualizo, me da un error que dice que props.response.names.length no está definido. Intenté usar la función useEffect para intentar actualizarlo, pero no funcionó. Apreciaría cualquier ayuda.
Mi archivo App.js (todavía quedan algunos restos de cuando ejecuta "npx create-react-app my-app"):
import './App.css'; import Tags from './Tags.js'; import React, { useState, useEffect } from 'react'; function App() { const makeRequest = async () => { try { let response = await fetch('RANDOM_API_URL'); let json = await response.json(); setResponse(json); } catch (error) { console.log(error); } } const [response, setResponse] = useState(makeRequest); useEffect(() => { setResponse(makeRequest); }, []); return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> <Tags response={response}></Tags> </div> ); } export default App;Mis etiquetas.js:
import './App.js'; function Tags(props) { const makeButtons = () => { let result = []; for (let i = 0; i < props.response.names.length; i++) { result.push(<button key={i}>hello</button>); } return result; } return ( <div> {makeButtons()} </div> ); } export default Tags;Su función makeRequest es asíncrona y establece el estado internamente. La razón por la que obtiene este error es que no siempre tiene una response.names . nombres para leer la longitud: solo tiene un objeto de response vacío.
Asegúrese de tener siempre la matriz de names disponible en el estado o evite representar su componente Etiquetas cuando los nombres no estén presentes en el estado.
Además, trate de evitar ser creativo con su matriz de dependencias, está ahí por una razón. Sin embargo, veo por qué no incluyeste makeRequest en él, ya que creas una nueva función en cada renderizado. Eso es algo a tener en cuenta cuando hace la transición de componentes de clase a componentes funcionales. Los métodos de clase son estables en las representaciones, las funciones declaradas en un componente funcional no lo son. Para imitar un método de clase, puede declarar funciones usando el gancho useCallback, pero nuevamente necesita incluir la matriz de dependencia. En su caso, puede simplemente crear la función asíncrona dentro de useEffect y luego llamarla.
const [response, setResponse] = useState({names: []}); // or const [response, setResponse] = useState(); // and then, in Tags: // function Tags({response = {names: []}) { useEffect(() => { const makeRequest = async () => { try { let response = await fetch('RANDOM_API_URL'); let json = await response.json(); setResponse(json); } catch (error) { console.log(error); } } makeRequest(); }, []); Proporcionar un accesorio de response predeterminado a las etiquetas asegurará que pueda leer la longitud de los names incluso antes de tener una respuesta.
Mirando más de cerca su componente Etiquetas, creo que debería ser algo como esto:
// don't import App.js here export default function Tags({response = {names: []}){ return ( <div> {response.names.map(name => { return <button key={name}>Hello {name}</button> })} </div> ) }No use el índice como clave, eso causará problemas si reorganiza los nombres en su matriz. Y supongo que desea proporcionar una función onClick a sus botones.