【问题标题】:How return url for a img tag with a function如何返回带有函数的img标签的url
【发布时间】:2022-01-16 16:02:18
【问题描述】:

所以我正在渲染一系列帖子。每个帖子都有一个与之关联的唯一用户 ID,我试图在另一个 API 调用中使用它来返回该帖子的用户图像。

目前我的设置是这样的

渲染帖子

             {data?.map((data) => (
                <div className="allpostsingle" key={data.id} onClick={() => sendTo(data.id)}>
                    <div className="allpostheader">
                        <img src={getUrl(data.user)}/>
                        <p>{data?.description}</p>
                    </div>
                </div>
              ))}

获取图片地址的函数

   const getUrl = async (id) => {
       let url = `http://127.0.0.1:8000/api/user/${id}/`
       const response = await fetch(url)
       const data = await response.json()
       const avatarurl = data.avatar
       return avatarurl
   }

这个问题是它返回一个承诺而不是 URL。我知道您可以将其设置为获取 url 的状态,但它不会对每个帖子都是唯一的?

我只是想知道如何解决这个问题,或者是否有其他方法可以解决这个问题。

【问题讨论】:

  • 需要调用getUrl方法getUrl()
  • 单独获取每个用户数据不是一个好主意,而是您可以在一个请求中获取所有用户数据然后呈现它
  • data.avatar的格式是什么?是url还是base64编码的图片数据?
  • 如果我认为可能的话,您必须包含用户数据才能在后端发布数据
  • 这不可能获取包含用户的所有帖子数据?如果不是,您只需要使用 promiss.all in useEffect 获取所有用户数据,然后获取帖子数据

标签: javascript reactjs fetch-api


【解决方案1】:

正如您所说,您的函数返回一个承诺而不是 URL(我遇到了同样的问题)。任何异步函数在按常规函数调用时都可能发生此问题(它们将返回 promise 而不是输出),除非您在常规 JavaScript 代码中使用回调函数或 async/await 函数。但是在 React 中,应该使用 UsEffect() hook 或 componentDidMount() 函数调用异步函数。如果你的反应组件是一个函数,你需要参考How to call an async function inside a UseEffect() in React?。或者如果你的 react 组件是一个类,请参考 How to call an async function in componentDidMount function?,以了解如何在 react 中准确使用它们来处理 React 组件中的异步函数。

【讨论】:

    【解决方案2】:

    您不能将getUrl() 的返回值分配给src 属性,因为它是异步的并返回Promise

    你可以做的是为用户头像维护另一个状态,当你的data 更新时。例如,创建一个解析用户头像的函数

    const resolveUserAvatars = async (users) => {
      const avatars = await Promise.allSettled(users.map(async ({ user }) => {
        const res = await fetch(`http://127.0.0.1:8000/api/user/${encodeURIComponent(user)}/`)
        if (!res.ok) {
          throw new Error(res.status)
        }
    
        return {
          user,
          avatar: (await res.json()).avatar
        }
      }))
    
      return Object.fromEntries(
        avatars
          .filter(({ status }) => status === "fulfilled")
          .map(({ value: { user, avatar } }) => [ user, avatar ])
        )
      )
    }
    

    然后在你的组件中...

    const [ data, setData ] = useState([]) // init as an array
    const [ avatars, setAvatars ] = useState({})
    // avatars will look like
    // { userId: imageUrl, ... }
    
    useEffect(() => {
      resolveUserAvatars.then(setAvatars)
    }, [ data ]) // execute when data changes
    

    在你的 JSX 中

    {data.map(({ id, user, description }) => (
      <div className="allpostsingle" key={id} onClick={() => sendTo(id)}>
        <div className="allpostheader">
          <img src={avatars[user] ?? "some/placeholder.png"}/>
          <p>{description}</p>
        </div>
     </div>
    ))}
    

    另见Promise.allSettled()

    【讨论】:

      【解决方案3】:

      单独获取每个用户的头像并为每个帖子发送请求并每次渲染帖子项目并不是一个好主意,如果您和后端 api 可能的话,只需在帖子中包含用户头像 url,以便轻松渲染它。 如果它不可能并且在 api 中有限制,您只需要在 useEffect 收到帖子数据后使用 Promiss.allSettled 获取所有发布用户数据并将用户数据附加到 postItem 或将其保存在单独的字典中然后设置状态以在其中呈现帖子和用户头像网址。

      【讨论】:

        【解决方案4】:

        它不起作用,因为在您的 getUrl 是异步函数时呈现同步反应。正如其他一些答案指出的那样,您可以获取 componentDidMountuseEffect 挂钩中的所有 url 并更新状态。

        如果您不想更改现有代码,也可以将 img 替换为您自己的 react 组件,以便它可以处理 promise。

        function Image({src}){
          const [actualSrc, setActualSrc] = useState('placeholder image link')
          useEffect(()=>{
            src.then((url) => setActualSrc(url));
          }, [src]);
          
          return <img src={actualSrc}/>
        }
        
        {data?.map((data) => (
          <div className="allpostsingle" key={data.id} onClick={() => sendTo(data.id)}>
            <div className="allpostheader">
              <Image src={getUrl(data.user)}/>
              <p>{data?.description}</p>
            </div>
          </div>
        ))}
        
        const getUrl = async (id) => {
               let url = `http://127.0.0.1:8000/api/user/${id}/`
               const response = await fetch(url)
               const data = await response.json()
               const avatarurl = data.avatar
               return avatarurl
        }
        

        这可能不是最好的性能,因为getUrl 将在每次渲染时发送一个新的 promise 引用。在这种情况下,最好将 idgetUrl 中的用户 ID)传递给 Image 组件,而不是一个承诺,并在其中包含获取实际 URL 的逻辑。

        function Image({userId}){
          const [actualSrc, setActualSrc] = useState('placeholder image link')
          useEffect(()=>{
            const getUrl = async (id) => {
               let url = `http://127.0.0.1:8000/api/user/${id}/`
               const response = await fetch(url)
               const data = await response.json()
               const avatarurl = data.avatar
               return avatarurl;
        
            getUrl(userId).then((url) => setActualSrc(url));
          }, [userId]);
          
          return <img src={actualSrc}/>
        }
        
        {data?.map((data) => (
          <div className="allpostsingle" key={data.id} onClick={() => sendTo(data.id)}>
            <div className="allpostheader">
              <Image userId={data.user} />
              <p>{data?.description}</p>
            </div>
          </div>
        ))}
        

        【讨论】:

        • 如果getUrl 是通过useCallback() 创建并依赖于data 会有帮助吗?
        • 不,useCallback 是要记住函数引用,而不是返回值。
        • 对了。 useCallback 对我来说仍然是灰色地带之一,我永远不知道你什么时候会使用它
        • 在这种情况下,最好将idgetUrl 中的用户 ID)传递给 Image 组件,而不是一个承诺,并具有获取其内部实际Url 的逻辑跨度>
        猜你喜欢
        • 2020-10-16
        • 1970-01-01
        • 2018-03-05
        • 1970-01-01
        • 2021-05-25
        • 1970-01-01
        • 1970-01-01
        • 2011-09-12
        • 2016-08-04
        相关资源
        最近更新 更多