首页 > 解决方案 > React img src 未从 aws s3 url 显示

问题描述

我有一个对象数组,其中每个对象的属性之一是文件名。然后,我使用该文件名从我编写的 API 端点获取预签名的 AWS S3 url。然后我想映射每个对象并显示有关它的信息,包括显示图像。无论出于何种原因,这都不起作用。大约一半的时间它只是不显示与图像相关的任何内容......不是实际图像,也不是任何“无图像”图标。另一半时间,当它确实工作时,它只会闪烁图像一秒钟,然后恢复为小的“无图像”图标。我不知道为什么这不起作用,因为我让它在另一个不需要映射的组件中工作,而且由于问题并没有始终如一地表现出来,我更加困惑......

这是不起作用的组件的代码:

export const MemeList = () => {
    const [state, setState] = useState<State>({
        memes: []
    });

    useEffect(() => {
        fetch(`${process.env.REACT_APP_BACKEND_URL}/memes/`)
            .then((response) => response.json())
            .then((memes) => {
                var newMemes = memes.map((meme) => {
                    fetch(`${process.env.REACT_APP_BACKEND_URL}/images/${meme.file}`)
                        .then((response) => response.json())
                        .then((image) => {
                            meme.image = image;
                        });
                    return meme;
                });
                setState({ memes: newMemes });
                console.log(newMemes);
            });
    }, []);

    return (
        <>
        {(state.memes.length == 0) ? (
            <p>Loading...</p>
        ) : (

        <div className="container">
                    <h2 className="comp-title">Featured Memes</h2>
                    <div className="featured-memes">
                        {
                            state.memes.slice(0,3).map((meme,i) => {
                                return (
                                    <div id={meme.id} className="meme">
                                        <div className="vote-container">
                                            <Vote className="vote" upvotes={meme.upvotes} category='memes' id={meme.id} upvoters={meme.upvoters} downvoters={meme.downvoters} />
                                        </div>
                                        <Link id={meme.id} to={`/memes/${meme.id}`}>
                                            <div className="meme-container">
                                                <div className="meme-head">
                                                    <h2 className="meme-title">{meme.title}</h2>
                                                </div>
                                                <div className="meme-body">
                                                    <img className="meme-img" src={meme.image} />
                                                </div>
                                            </div>
                                        </Link>
                                    </div>
                                )
                            })
                        }
                    </div>
            </div>
        )}
    </>
    );

这是工作组件的代码:

    useEffect(() => {
        setLoading(true);
        fetch(`${process.env.REACT_APP_BACKEND_URL}/memes/${memeId}`)
            .then((response) => response.json())
            .then((meme) => {
                fetch(`${process.env.REACT_APP_BACKEND_URL}/images/${meme.file}`)
                    .then((response) => response.json())
                    .then((image) => {
                        setState({ ...state, id: meme.id, title: meme.title, upvotes: meme.upvotes, upvoters: meme.upvoters, downvoters: meme.downvoters, comments: meme.comments, file: image, posterAddress: meme.posterAddress, category: meme.category, stakeWithdrawn: meme.stakeWithdrawn });
                    })
                    .then(setLoading(false))
                    .catch((err) => window.alert(err));
            })
            .catch((err) => window.alert(err));
    }, []);

    return (
        <>
            {(state.file == '') ? (
                <p>Loading</p>
            ) : (
                <div className="meme-comp-container">
                    <div className="meme-comp-vote-container">
                        <Vote className="vote" upvotes={state.upvotes} category="memes" id={state.id} upvoters={state.upvoters} downvoters={state.downvoters} />
                    </div>
                    <div className="meme-comp-meme-container">
                        <div className="meme-comp-meme-head">
                            <h2 className="meme-comp-title">{state.title}</h2>
                        </div>
                        <div className="meme-comp-meme-body">
                            <img className="meme-comp-meme-img" src={state.file} />
                        </div>
                    </div>
                </div>
            )}
        </>
    )

**编辑:**快速更新,所以我想我找到了问题但不知道如何解决它。在 useEffect 钩子中,如果我打印“newMemes”,它会正确显示所有内容,但如果我映射 newMemes 并仅打印它显示未定义的图像属性。

    useEffect(() => {
        fetch(`${process.env.REACT_APP_BACKEND_URL}/memes/`)
            .then((response) => response.json())
            .then((memes) => {
                var newMemes = memes.map((meme) => {
                    fetch(`${process.env.REACT_APP_BACKEND_URL}/images/${meme.file}`)
                        .then((response) => response.json())
                        .then((image) => {
                            meme.image = image;
                        });
                    return meme;
                });
                setState({ memes: newMemes });
                newMemes.slice(0,3).map((newMeme) => console.log(newMeme.image));
                console.log(newMemes);
            });
    }, []);

给出输出:undefined undefined undefined [0: category: "meme" comments: 0 createdAt: "2021-09-11T23:12:58.956Z" downvoters: [] file: "corp_site_logo_1631401978598.png" id: 11 image: "https: // - - .s3.amazonaws.com/corp_site_logo_1631401978598.png?AWSAccessKeyId= &Expires= &Signature=W% %3D" posterAddress: "0x8a8b5a97978db4a54367d7dcf6a50980990f2373" 58.956Z" 支持者:['0x8a8b5a97978db4a54367d7dcf6a50980990f2373'], 1: ..., 2: ..., ...]

标签: reactjsimageamazon-s3

解决方案


如果您比较工作代码和非工作代码,您应该立即注意到差异。在工作代码中,您请求单个 meme 并单独获取其图像,并将状态更新入队。在非工作代码中,您请求多个 meme,单独获取图像,在这些请求可能解决之前将状态更新排队,然后改变状态对象。这就是为什么您会看到与第一个不工作的代码片段不一致的结果。

您可以单独请求每个图像并使用功能状态更新将增强meme对象添加到状态。

useEffect(() => {
  fetch(`${process.env.REACT_APP_BACKEND_URL}/memes/`)
    .then((response) => response.json())
    .then((memes) => {
      memes.forEach(meme => 
        fetch(`${process.env.REACT_APP_BACKEND_URL}/images/${meme.file}`)
          .then((response) => {
            if (!response.ok) throw new Error('response not ok');
            return response.json();
          })
          .then((image) => {
            setState(prevState => ({
              ...prevState,
              memes: [
                ...prevState.memes,
                {
                  ...meme,
                  image,
                }
              ],
            }));
          })
          .catch(error => {
            // handle error
          })
          .finally(() => {
            // clear any loading state
          });
      );
}, []);

推荐阅读