Kim*_*Kim 3 amazon-dynamodb reactjs aws-amplify react-hooks
我似乎总是遇到这个问题,而且我从来没有足够的能力来解决这个问题。
我将基于下面的代码块使用一个示例,该示例表示使用useState和useEffect的React功能组件。
设定
AWS S3存储桶中有一些mp3文件。这些文件已经过处理,因此文件名的格式为“ artist ||| title.mp3”。
这些歌曲的元数据已存储在DyanamoDB表中,其分区键为“ artist”,排序键为“ title”。
有一个函数getS3songs,它以对象数组的形式异步获取所有歌曲的列表,并带有键“ key”,该键保存上面#2的文件名。
该功能forEach在文件列表上运行,并从每个文件中解析出“艺术家”和“标题”,然后执行单独的异步API调用,以通过API网关从DynamoDB表中获取每首歌曲的元数据。
使用React useState挂钩在状态下创建一个数组“ songs”。
我要尝试做的 事情最终我要做的事情是使用useEffect钩子为步骤4中每首歌曲返回的元数据填充“歌曲”数组。
问题 以下代码块导致运行无限循环,因为[歌曲]被设置为useEffect挂钩的第二个参数。
我已经尝试了几种变体,但是我相信以下内容代表了我要解决的问题的症结所在。
注意这里的棘手部分不是“ s3Songs”的初始获取。可以将其作为单个对象直接放入状态。棘手的部分是,有多个对API的异步调用,以获取每个文件的元数据并将这些对象中的每个对象放入“歌曲”数组中。这就是我的头脑。
问题 此问题的最佳或推荐模式是什么?
import React, { useEffect, useState } from "react";
import Amplify, { API, Storage } from "aws-amplify";
import awsconfig from "./aws-exports";
Amplify.configure(awsconfig);
const StackOverflowExample = () => {
const [songs, setSongs] = useState([]);
useEffect(() => {
const getS3Songs = async () => {
const s3Songs = await Storage.list("", { level: "public" });
s3Songs.forEach(async song => {
const artist = song.key.split(" ||| ")[0];
const title = song.key.split(" ||| ")[1].slice(0, -4);
const metadata = await API.get("SongList", `/songs/${artist}/${title}`);
// setSongs([...songs, metadata]); <= causes loop
});
};
getS3Songs();
}, [songs]); // <= "songs" auto added by linter in create-react-app in vsCode. Removing "songs" and disabling linter on that line doesn't help.
const renderSongs = () => {
return songs.map(song => {
return <li>{song.title}</li>;
});
};
return (
<div>
<ul>{renderSongs()}</ul>
</div>
);
};
export default StackOverflowExample;
Run Code Online (Sandbox Code Playgroud)
根据Will关于分离到两个useEffect挂钩的评论进行更新。我尝试了下面的方法,我在考虑是否可以将a Promise.all放入混合中,以便在第二个挂钩中返回songMetadata数组,当所有metadatapromise都已解决后,填充它,我会接近的。
useEffect(() => {
console.log("Effect!");
const getS3Files = async () => {
try {
const response = await Storage.list("", { level: "public" });
setS3FileList(response);
} catch (e) {
console.log(e);
}
};
getS3Files();
}, []);
useEffect(() => {
console.log("Effect!");
const songMetadata = [];
s3FileList.forEach(async song => {
const artist = song.key.split(" ||| ")[0];
const title = song.key.split(" ||| ")[1].slice(0, -4);
const metadata = await API.get(
"SongList",
`/songs/object/${artist}/${title}`
);
console.log(metadata);
songMetadata.push(metadata);
});
setSongs(songMetadata);
}, [s3FileList]);
Run Code Online (Sandbox Code Playgroud)
看来您需要通过添加另一个来分离两个获取的关注点useEffect。然后,您可以使用它Promise.all来等待第二个api调用的响应完成,然后再更新歌曲。
const StackOverflowExample = () => {
const [songs, setSongs] = useState([]);
const [s3Songs, setS3Songs] = useState([]);
const getSong = async song => {
const artist = song.key.split(" ||| ")[0];
const title = song.key.split(" ||| ")[1].slice(0, -4);
const metadata = await API.get("SongList", `/songs/${artist}/${title}`);
return metadata
}
useEffect(() => {
const getS3Songs = async () => {
const s3s = await Storage.list("", { level: "public" })n
setS3Songs(s3s);
};
getS3Songs();
}, []);
useEffect(()=>{
const pending = s3Songs.map(song=>getSong(song))
Promise.all(pending).then(songs=>setSongs(songs));
}, [s3Songs])
const renderSongs = () => {
return songs.map(song => {
return <li>{song.title}</li>;
});
};
return (
<div>
<ul>{renderSongs()}</ul>
</div>
);
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
461 次 |
| 最近记录: |