#reactjs #axios #react-hooks
#reactjs #axios #реагирующие крючки
Вопрос:
Я хотел бы получить глобальную информацию от пользователя Github и его репозиториев (и закрепленные репозитории будут потрясающими). Я пытаюсь сделать это с помощью async await, но это правильно? У меня 4 раза повторный запуск (4 раза журнал консоли). Можно ли дождаться повторного запуска всего компонента, когда будут извлечены все данные?
function App() {
const [data, setData] = useState(null);
const [repos, setRepos] = useState(null);
useEffect(() => {
const fetchData = async () => {
const respGlobal = await axios(`https://api.github.com/users/${username}`);
const respRepos = await axios(`https://api.github.com/users/${username}/repos`);
setData(respGlobal.data);
setRepos(respRepos.data);
};
fetchData()
}, []);
if (data) {
console.log(data, repos);
}
return (<h1>Hello</h1>)
}
Комментарии:
1. Я никогда раньше не видел React Hooks, упс. Но вы смотрели документацию по async series ? Это позволяет вам выполнять один вызов за другим, затем вы можете
setState
выполнять обратный вызов после успешного выполнения каждого запроса axios.
Ответ №1:
Несколько обновлений состояния отправляются пакетно, но только в том случае, если это происходит синхронно из обработчиков событий, а не setTimeouts
или async-await wrapped methods
.
Это поведение аналогично классам, и поскольку в вашем случае оно выполняет два цикла обновления состояния из-за двух вызовов обновления состояния
Итак, сначала у вас есть начальный рендеринг, а затем у вас есть два обновления состояния, поэтому компонент отображается три раза.
Поскольку два состояния в вашем случае связаны, вы можете создать объект и обновить их вместе следующим образом:
function App() {
const [resp, setGitData] = useState({ data: null, repos: null });
useEffect(() => {
const fetchData = async () => {
const respGlobal = await axios(
`https://api.github.com/users/${username}`
);
const respRepos = await axios(
`https://api.github.com/users/${username}/repos`
);
setGitData({ data: respGlobal.data, repos: respGlobal.data });
};
fetchData();
}, []);
console.log('render');
if (resp.data) {
console.log("d", resp.data, resp.repos);
}
return <h1>Hello</h1>;
}
Комментарии:
1. ответ работает, но есть гораздо более элегантный способ. Вы можете просто использовать
const [respGlobal, respRepos] = await Promise.all([ axios(
api.github.com/users /$ {имя пользователя}), axios(
api.github.com/users /$ {имя пользователя}/ репозитории) ]);
2. Как бы вы это сделали, если 2 вызова api не связаны? В этом случае вызывается /users, и мы ждем его завершения, прежде чем вызывать /users /x / repos. Что, если данные независимы друг от друга? Я не хочу, чтобы один ждал другого. Есть ли шаблон для этого? Я думаю, что такая же «проблема» существует с ответом @ FabianHinsenkamp
Ответ №2:
Подумал, что я бы попробовал это, потому что приведенный выше ответ хорош, однако мне нравится чистота.
import React, { useState, useEffect } from 'react'
import axios from 'axios'
const Test = () => {
const [data, setData] = useState([])
useEffect(() => {
(async () => {
const data1 = await axios.get('https://jsonplaceholder.typicode.com/todos/1')
const data2 = await axios.get('https://jsonplaceholder.typicode.com/todos/2')
setData({data1, data2})
})()
}, [])
return JSON.stringify(data)
}
export default Test
Использование самозапускающейся функции устраняет дополнительный этап вызова функции в useEffect
, который иногда может вызывать ошибки Promise в IDE, таких как WebStorm и PhpStorm.
Ответ №3:
function App() {
const [resp, setGitData] = useState({ data: null, repos: null });
useEffect(() => {
const fetchData = async () => {
const respGlobal = await axios(
`https://api.github.com/users/${username}`
);
const respRepos = await axios(
`https://api.github.com/users/${username}/repos`
);
setGitData({ data: respGlobal.data, repos: respGlobal.data });
};
fetchData();
}, []);
console.log('render');
if (resp.data) {
console.log("d", resp.data, resp.repos);
}
return <h1>Hello</h1>;
}
he made some mistake here:
setGitData({ data: respGlobal.data, repos: respGlobal.data(respRepos.data //it should be respRepos.data});
Ответ №4:
Для других исследователей (живая демонстрация):
import React, { useEffect, useState } from "react";
import { CPromise, CanceledError } from "c-promise2";
import cpAxios from "cp-axios";
function MyComponent(props) {
const [error, setError] = useState("");
const [data, setData] = useState(null);
const [repos, setRepos] = useState(null);
useEffect(() => {
console.log("mount");
const promise = CPromise.from(function* () {
try {
console.log("fetch");
const [respGlobal, respRepos] = [
yield cpAxios(`https://api.github.com/users/${props.username}`),
yield cpAxios(`https://api.github.com/users/${props.username}/repos`)
];
setData(respGlobal.data);
setRepos(respRepos.data);
} catch (err) {
console.warn(err);
CanceledError.rethrow(err); //passthrough
// handle other errors than CanceledError
setError(err "");
}
}, []);
return () => {
console.log("unmount");
promise.cancel();
};
}, [props.username]);
return (
<div>
{error ? (
<span>{error}</span>
) : (
<ul>
<li>{JSON.stringify(data)}</li>
<li>{JSON.stringify(repos)}</li>
</ul>
)}
</div>
);
}