让 React JS 在 运行 之前等待异步函数完成
Make React JS await for a async func to complete before running
我试图让 react 在 axios get 请求完成之前不加载。我对周围的反应很粗鲁,所以提前道歉。
我正在获取一组对象
const { dogBreedsTest } = useApplicationData()
我需要它成为我的一个状态的默认值
const [dogBreeds, updateDogBreeds] = useState(dogBreedsTest);
但是,我收到一条错误消息,指出在我的应用程序启动的第一次迭代中我的值变为 null。在我的应用程序尝试使用它之前,如何确保我的值已完成我的请求?
这是我获取 useApplicationData() 数据的方式
const [dogBreedsTest, setDogBreeds] = useState(null);
const getDogBreeds = async () => {
try{
const { data } = await axios.get('https://dog.ceo/api/breeds/list/all')
if(data) {
const newDogList = generateDogsArray(data['message'])
const generatedDogs = selectedDogs(newDogList)
setDogBreeds(generatedDogs)
}
} catch(err) {
console.log(err);
}
}
useEffect(() => {
getDogBreeds()
}, []);
return {
dogBreedsTest,
setDogBreeds
}
我正在导入我的应用程序并使用:
import useApplicationData from "./hooks/useApplicationData";
const { dogBreedsTest } = useApplicationData()
const [dogBreeds, updateDogBreeds] = useState(dogBreedsTest[0]);
const [breedList1, updateBreedList1] = useState(dogBreedsTest[0])
function handleOnDragEnd(result) {
if (!result.destination) return;
const items = Array.from(dogBreeds);
const [reorderedItem] = items.splice(result.source.index, 1);
items.splice(result.destination.index, 0, reorderedItem);
for (const [index, item] of items.entries()) {
item['rank'] = index + 1
}
updateDogBreeds(dogBreedsTest[0]);
updateBreedList1(dogBreedsTest[0])
}
return (
<div className="flex-container">
<div className="App-header">
<h1>Dog Breeds 1</h1>
<DragDropContext onDragEnd={handleOnDragEnd}>
<Droppable droppableId="characters">
{(provided) => (
<ul className="dogBreeds" {...provided.droppableProps} ref={provided.innerRef}>
{breedList1?.map(({id, name, rank}, index) => {
return (
<Draggable key={id} draggableId={id} index={index}>
{(provided) => (
<li ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
<p>
#{rank}: { name }
</p>
</li>
)}
</Draggable>
);
})}
{provided.placeholder}
</ul>
)}
</Droppable>
</DragDropContext>
</div>
)
错误:TypeError: Cannot read property 'map' of null
(我在后面的程序中映射数据)
问题是,react 先渲染然后 运行useEffect(),所以如果你不想在 axios 之前渲染任何东西,你需要告诉 react,第一个渲染是空。
你的地图函数在哪里,看代码?给你看?
我想你的数据首先是空的。所以你可以使用类似的东西。
if(!data) return null
第二个选项:
In your map try this:
{breedList1 === null
? null
: breedList1.map(({id, name, rank}, index) => (
<Draggable
key={id} draggableId={id} index={index}>
{(provided) => (
<li ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
<p>
#{rank}: { name }
</p>
</li>
)}
</Draggable> ))}
你有 null,因为你的 axios 是异步的,并且反应在任何效果之前尝试渲染。所以如果你说 react 列表是空的,react 会在第二次渲染和加载 api 中的数据。
选项 1 使用可选的链接运算符
dogBreedsTest?.map()
如果 dogBreedsTest
是数组
,则选项 2 检查 return
retrun (<>
{Array.isArray(dogBreedsTest) && dogBreedsTest.map()}
</>)
选项 3 return早
if (!Array.isArray(dogBreedsTest)) return null
retrun (<>
{dogBreedsTest.map()}
</>)
选项 4 设置初始状态
const [dogBreedsTest, setDogBreeds] = useState([]);
您还可以添加加载状态并添加加载微调器或类似的东西:
const [dogBreedsTest, setDogBreeds] = useState(null);
const [loading, setLoading] = useState(true)
const getDogBreeds = async () => {
setLoading(true)
try{
const { data } = await axios.get('https://dog.ceo/api/breeds/list/all')
if(data) {
const newDogList = generateDogsArray(data['message'])
const generatedDogs = selectedDogs(newDogList)
setDogBreeds(generatedDogs)
}
} catch(err) {
console.log(err);
}
setLoading(false)
}
useEffect(() => {
getDogBreeds()
}, []);
return {
dogBreedsTest,
loading,
setDogBreeds
}
编辑
尝试使用 useEffect
挂钩在设置 dogBreedsTest
时更新状态。
const { dogBreedsTest } = useApplicationData()
const [dogBreeds, updateDogBreeds] = useState(dogBreedsTest?.[0] ?? []);
const [breedList1, updateBreedList1] = useState(dogBreedsTest?.[0] ?? [])
useEffect(() => {
updateDogBreeds(dogBreedsTest?.[0] ?? [])
updateBreedList1(dogBreedsTest?.[0] ?? [])
}, [dogBreedsTest])
const getDogBreeds = async () => {
try {
const { data } = await axios.get('https://dog.ceo/api/breeds/list/all')
if(data) {
const newDogList = generateDogsArray(data['message'])
const generatedDogs = selectedDogs(newDogList)
setDogBreeds(generatedDogs)
}
} catch(err) {
console.log(err);
}
}
useEffect(() => {
getDogBreeds() // -> you are not awaiting this
}, []);
改为这样做
useEffect(() => {
axios.get('https://dog.ceo/api/breeds/list/all')
.then(res => {
const newDogList = generateDogsArray(res.data['message']);
const generatedDogs = selectedDogs(newDogList);
setDogBreeds(generatedDogs);
})
.catch(err => console.log(err));
}, []);
我知道这看起来很糟糕,但我认为您不应该在 useEffect
中使用 async/await
在您的应用程序中使用它
useEffect
将在 dogBreedsTest
更改时更新。为了使其工作,从 null
值开始,并在 async
操作完成后将它们更新为正确的初始值。
const { dogBreedsTest } = useApplicationData();
const [dogBreeds, updateDogBreeds] = useState(null);
const [breedList1, updateBreedList1] = useState(null);
useEffect(() => {
updateDogBreeds(dogBreedsTest[0]);
updateBreedList1(dogBreedsTest[0]);
}, [dogBreedsTest]);
我试图让 react 在 axios get 请求完成之前不加载。我对周围的反应很粗鲁,所以提前道歉。
我正在获取一组对象
const { dogBreedsTest } = useApplicationData()
我需要它成为我的一个状态的默认值
const [dogBreeds, updateDogBreeds] = useState(dogBreedsTest);
但是,我收到一条错误消息,指出在我的应用程序启动的第一次迭代中我的值变为 null。在我的应用程序尝试使用它之前,如何确保我的值已完成我的请求?
这是我获取 useApplicationData() 数据的方式
const [dogBreedsTest, setDogBreeds] = useState(null);
const getDogBreeds = async () => {
try{
const { data } = await axios.get('https://dog.ceo/api/breeds/list/all')
if(data) {
const newDogList = generateDogsArray(data['message'])
const generatedDogs = selectedDogs(newDogList)
setDogBreeds(generatedDogs)
}
} catch(err) {
console.log(err);
}
}
useEffect(() => {
getDogBreeds()
}, []);
return {
dogBreedsTest,
setDogBreeds
}
我正在导入我的应用程序并使用:
import useApplicationData from "./hooks/useApplicationData";
const { dogBreedsTest } = useApplicationData()
const [dogBreeds, updateDogBreeds] = useState(dogBreedsTest[0]);
const [breedList1, updateBreedList1] = useState(dogBreedsTest[0])
function handleOnDragEnd(result) {
if (!result.destination) return;
const items = Array.from(dogBreeds);
const [reorderedItem] = items.splice(result.source.index, 1);
items.splice(result.destination.index, 0, reorderedItem);
for (const [index, item] of items.entries()) {
item['rank'] = index + 1
}
updateDogBreeds(dogBreedsTest[0]);
updateBreedList1(dogBreedsTest[0])
}
return (
<div className="flex-container">
<div className="App-header">
<h1>Dog Breeds 1</h1>
<DragDropContext onDragEnd={handleOnDragEnd}>
<Droppable droppableId="characters">
{(provided) => (
<ul className="dogBreeds" {...provided.droppableProps} ref={provided.innerRef}>
{breedList1?.map(({id, name, rank}, index) => {
return (
<Draggable key={id} draggableId={id} index={index}>
{(provided) => (
<li ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
<p>
#{rank}: { name }
</p>
</li>
)}
</Draggable>
);
})}
{provided.placeholder}
</ul>
)}
</Droppable>
</DragDropContext>
</div>
)
错误:TypeError: Cannot read property 'map' of null
(我在后面的程序中映射数据)
问题是,react 先渲染然后 运行useEffect(),所以如果你不想在 axios 之前渲染任何东西,你需要告诉 react,第一个渲染是空。
你的地图函数在哪里,看代码?给你看?
我想你的数据首先是空的。所以你可以使用类似的东西。
if(!data) return null
第二个选项:
In your map try this:
{breedList1 === null
? null
: breedList1.map(({id, name, rank}, index) => (
<Draggable
key={id} draggableId={id} index={index}>
{(provided) => (
<li ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
<p>
#{rank}: { name }
</p>
</li>
)}
</Draggable> ))}
你有 null,因为你的 axios 是异步的,并且反应在任何效果之前尝试渲染。所以如果你说 react 列表是空的,react 会在第二次渲染和加载 api 中的数据。
选项 1 使用可选的链接运算符
dogBreedsTest?.map()
如果 dogBreedsTest
是数组
,则选项 2 检查 return
retrun (<>
{Array.isArray(dogBreedsTest) && dogBreedsTest.map()}
</>)
选项 3 return早
if (!Array.isArray(dogBreedsTest)) return null
retrun (<>
{dogBreedsTest.map()}
</>)
选项 4 设置初始状态
const [dogBreedsTest, setDogBreeds] = useState([]);
您还可以添加加载状态并添加加载微调器或类似的东西:
const [dogBreedsTest, setDogBreeds] = useState(null);
const [loading, setLoading] = useState(true)
const getDogBreeds = async () => {
setLoading(true)
try{
const { data } = await axios.get('https://dog.ceo/api/breeds/list/all')
if(data) {
const newDogList = generateDogsArray(data['message'])
const generatedDogs = selectedDogs(newDogList)
setDogBreeds(generatedDogs)
}
} catch(err) {
console.log(err);
}
setLoading(false)
}
useEffect(() => {
getDogBreeds()
}, []);
return {
dogBreedsTest,
loading,
setDogBreeds
}
编辑
尝试使用 useEffect
挂钩在设置 dogBreedsTest
时更新状态。
const { dogBreedsTest } = useApplicationData()
const [dogBreeds, updateDogBreeds] = useState(dogBreedsTest?.[0] ?? []);
const [breedList1, updateBreedList1] = useState(dogBreedsTest?.[0] ?? [])
useEffect(() => {
updateDogBreeds(dogBreedsTest?.[0] ?? [])
updateBreedList1(dogBreedsTest?.[0] ?? [])
}, [dogBreedsTest])
const getDogBreeds = async () => {
try {
const { data } = await axios.get('https://dog.ceo/api/breeds/list/all')
if(data) {
const newDogList = generateDogsArray(data['message'])
const generatedDogs = selectedDogs(newDogList)
setDogBreeds(generatedDogs)
}
} catch(err) {
console.log(err);
}
}
useEffect(() => {
getDogBreeds() // -> you are not awaiting this
}, []);
改为这样做
useEffect(() => {
axios.get('https://dog.ceo/api/breeds/list/all')
.then(res => {
const newDogList = generateDogsArray(res.data['message']);
const generatedDogs = selectedDogs(newDogList);
setDogBreeds(generatedDogs);
})
.catch(err => console.log(err));
}, []);
我知道这看起来很糟糕,但我认为您不应该在 useEffect
async/await
在您的应用程序中使用它
useEffect
将在 dogBreedsTest
更改时更新。为了使其工作,从 null
值开始,并在 async
操作完成后将它们更新为正确的初始值。
const { dogBreedsTest } = useApplicationData();
const [dogBreeds, updateDogBreeds] = useState(null);
const [breedList1, updateBreedList1] = useState(null);
useEffect(() => {
updateDogBreeds(dogBreedsTest[0]);
updateBreedList1(dogBreedsTest[0]);
}, [dogBreedsTest]);