Creé un punto final en Express que maneja las solicitudes de obtención. Desde un componente de reacción, hago una solicitud de obtención a dicho punto final usando axios. Quiero almacenar los datos en un objeto en mi clase de Componente para que se pueda acceder a ellos varias veces (onComponentDidLoad, múltiples controladores de eventos onClick, etc.). ¿Hay alguna manera de almacenar los datos fuera de la promesa de axios y/o preservar la promesa para que pueda hacer varias llamadas .then sin que se cumpla la promesa?
Intenté usar setState(), devolver la promesa y devolver los datos reales de la solicitud de obtención.
Esto es lo que tengo ahora mismo:
constructor { super(); this.myData = []; this.getData = this.getData.bind(this); this.storeData = this.storeData.bind(this); this.showData = this.showData.bind(this); } // Store data storeData = (data) => {this.myData.push(data)}; // Get data from API getData() { axios .get('/someEndpoint') .then(response => { let body = response['data']; if(body) { this.storeData(body); } }) .catch(function(error) { console.log(error); }); } showData() { console.log(this.myData.length); // Always results in '0' } componentDidMount = () => { this.getData(); // Get data this.showData(); // Show Data } render() { return( <Button onClick={this.showData}> Show Data </Button> ); }Editar Estaba incorrecto en mi pregunta, almacenar la promesa y luego hacer múltiples llamadas .then funciona. Lo tenía mal formateado cuando lo probé.
Si solo almacena la promesa localmente y accede a ella como una promesa, debería funcionar bien.
getData() { // if request has already been made then just return the previous request. this.data = this.data || axios.get(url) .then( response => response.data) .catch(console.log) return this.data } showData() { this.getData().then(d => console.log('my data is', data)); }Este código no funcionará del todo porque está intentando mostrar los datos sin esperar a que se resuelva:
componentDidMount = () => { this.getData(); this.showData(); } Como insinuó en su publicación original, deberá extraer los datos de Promise y no hay forma de hacerlo de manera sincrónica. Lo primero que puede hacer es simplemente almacenar la Promesa original y acceder a ella cuando sea necesario. Las Promesas se pueden then() varias veces:
class C extends React.Component { state = { promise: Promise.reject("not yet ready") }; showData = async () => { // You can now re-use this.state.promise. // The caveat here is that you might potentially wait forever for a promise to resolve. console.log(await this.state.promise); } componentDidMount() { const t = fetchData(); this.setState({ promise: t }); // Take care not to re-assign here this.state.promise here, as otherwise // subsequent calls to t.then() will have the return value of showData() (undefined) // instead of the data you want. t.then(() => this.showData()); } render() { const handleClick = () => { this.showData(); }; return <button onClick={handleClick}>Click Me</button>; } }Otro enfoque sería tratar de mantener su componente lo más sincrónico posible al limitar la asincronía completamente a la función fetchData(), lo que puede hacer que su componente sea un poco más fácil de razonar:
class C extends React.Component { state = { status: "pending", data: undefined }; async fetchData(abortSignal) { this.setState({ status: "pending" }); try { const response = await fetch(..., { signal: abortSignal }); const data = await response.json(); this.setState({ data: data, status: "ok" }); } catch (err) { this.setState({ error: err, status: "error" }); } finally { this.setState({ status: "pending" }); } } showData() { // Note how we now do not need to pollute showData() with asyncrony switch (this.state.status) { case "pending": ... case "ok": console.log(this.state.data); case "error": ... } } componentDidMount() { // Using an instance property is analogous to using a ref in React Hooks. // We don't want this to be state because we don't want the component to update when the abort controller changes. this.abortCtrl = new AbortController(); this.fetchData(this.abortCtrl.signal); } componentDidUnmount() { this.abortCtrl.abort(); } render() { return <button onClick={() => this.showData()}>Click Me</button> } }