Функция не вызывается при использовании async / await в React - PullRequest
0 голосов
/ 29 апреля 2020

Я пытаюсь выполнить асинхронный вызов при открытии моего модального режима. Мне нужен этот вызов, потому что он извлек изображения, и для получения ответа потребуется около 5 секунд.

Таким образом, модал должен показывать сначала данные, а затем, когда выборка завершена, он также должен показывать данные выборки.

Моя проблема в том, что в момент вызова функции с этим () => {this.fetchImages(id) она не вызывается. Я предполагаю, что это потому, что функция назначается, а не вызывается.

Но когда я вызываю функцию fetchImages() без () =>, я получаю эту ошибку:

Invariant Violation: Minified React error #31

Это мой код, несущественная часть была удалена для простоты:

renderModal = () => {
    ...
    return (

      <Modal open={openModal != null} onClose={this.closeModal}
             little showCloseIcon={true} styles={modalStyles}>
        <div className="item">
          ...
          {() => {this.fetchImages(id)
             .then(r => console.log("Fetch result" + r))}}
        </div>
      </Modal>
    );
  }

fetchImages = async (id) =>{
    console.log("Request started")
    try{
      let myImages = null;
      if(typeof(id) !== 'undefined' && id != null) {
        myImages = await fetchImages(id);
        console.log("Images: " + myImages);
        return (
          <div>
            {Array.isArray(myImages) && myImages.length !== 0 && myImages.map((item, key) =>
              <p>Image name: {item.name}, En device name: {item.name.en_US}</p>
            )}
          </div>
        );
      }
    } catch (e) {
      console.log("Failed")
    }
  }

РЕДАКТИРОВАТЬ Изменяя код, предложенный jack.benson и GalAbra, я столкнулся с проблемой, в которой застрял в бесконечном l oop. Я добавлю новый код:

Как только страница загрузится, renderModal вызывается в render() методе:

{this.renderModal()}

Тогда у меня есть кнопка, которая будет показывать мод с Модал содержит строку:

<Modal open={openModal != null} onClose={this.closeModal}
         little showCloseIcon={true} styles={modalStyles}>

Вызывается отсюда:

myMethod = (task) => {
    ...

    return (
  <div {...attrs}>
      ...
      <button onClick={() => {this.showModal(documents[0])}}>{translate('show_more')} &raquo;</button>
      </div>
      }
    </div>
  </div>
);};

И часть, чтобы показать модал:

  showModal = (document) => {
    this.setState({ modalOpen: document });
  };

А теперь новый renderModal():

renderModal = () => {
      const doThing = async () => {
      try {
        const newImages = await downloadDeviceImages(id);
        return { data: newImages };
      } catch (e) {
        console.log("Failed")
      }
    };

    const test = async (id) => {
      const imageData = await doThing(id);
      console.log("after", imageData.data);
      this.setState({
        imageData: imageData.data
      });
    };

    if(typeof(id) !== 'undefined' && id != null) {test(id);}

    return (

      <Modal open={openModal != null} onClose={this.closeModal}
             little showCloseIcon={true} styles={modalStyles}>
        <div className="item">
          ...
      <div>
        {this.state.imageData.map((item, key) =>
          <p>Device name: {item.name}, En device name: {item.name.en_US}</p>
        )}
      </div>
      </Modal>
    );
  }

Основная часть здесь заключается в том, что кнопка будет нажиматься несколько раз, и у нее может быть разный идентификатор при каждом щелчке, поэтому каждый раз следует делать новый запрос.

Ответы [ 2 ]

1 голос
/ 29 апреля 2020

Итак, this.fetchImages(id) возвращает Обещание , которое React не знает, как сделать. Вот почему вы получаете ошибку. Когда вы заключаете его в () => { ... }, вы фактически создаете функцию, которая упаковывает ваше обещание, но вы никогда не вызываете функцию . Вот почему это не называется. Это все равно что сказать:

function aThingToDo() {
  console.log('Did thing!')
}

function doThing() {
  aThingToDo();
}

Обратите внимание, что я объявил функции, и ни одна из них не была вызвана. Вам нужно вызвать функцию, явно добавив скобки после нее (например, doThing()).

Это объясняет вашу ошибку и тот факт, что ваша функция не вызывается. Теперь, как справиться с этим. Вы хотите обернуть свою выборку в useEffect (как должно быть сделано async во время render). Затем, если вам нужны данные во время render, вы можете установить состояние после его завершения, что вызовет повторную визуализацию. Я собрал быстрый пример того, о чем я говорю:

import React, { useEffect, useState } from "react";
import "./styles.css";

const doThing = async () => {
  console.log("doing a thing");
  return { data: "My data" };
};

export default function App() {
  const [data, setData] = useState("");

  useEffect(() => {
    const test = async () => {
      const data = await doThing();
      setData(data);
    };
    test();
  }, []);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      {data && data.data}
    </div>
  );
}

Вот аналогичный пример с использованием компонентов класса.

import React from "react";
import "./styles.css";

const doThing = async () => {
  console.log("doing a thing");
  return { message: "New message" };
};

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: "Default message"
    };
  }

  componentDidMount() {
    const test = async () => {
      const data = await doThing();
      console.log("after", data.message);
      this.setState({
        data: data.message
      });
    };
    test();
  }

  render() {
    console.log("rendering", this.state.data);
    return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
        {this.state.data}
      </div>
    );
  }
}

Надеюсь, это поможет!

1 голос
/ 29 апреля 2020

В настоящее время ваш код содержит асинхронную функцию c, смешанную с HTML элементами.

Вместо этого у вас есть состояние React, которое позаботится о повторном рендеринге после выборки данных:

const ModalWrapper = ({ id }) => {
    const [myImages, setMyImages] = React.useState([]);

    const fetchImages = async (id) => {
      try {
        const newImages = await fetchImagesCall(id);
        setMyImages(newImages); // Will trigger a re-render of the component, with the new images
      } catch (e) {
        console.log("Failed")
      }
    }

    React.useEffect(() => {
        fetchImages(id);
    }, []); // This empty array makes sure the `fetchImages` call will occur only once the component is mounted

    return (
      <Modal>
        <div className="item">
          <div>
            {myImages.map((item, key) =>
              <p>Image name: {item.name}, En device name: {item.name.en_US}</p>
            )}
          </div>
        </div>
      </Modal>
    );
}
...