así que estoy mostrando una serie de publicaciones. Cada publicación tiene una identificación de usuario única asociada, que estoy tratando de usar en otra llamada API para devolver la imagen del usuario para esa publicación.
Actualmente lo tengo configurado así
Representación de la publicación
{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> ))}La función para obtener la URL de la imagen.
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 }El problema con esto es que devuelve una promesa en lugar de la URL. Sé que puede configurarlo en un estado para obtener la URL, pero entonces no sería único para cada publicación.
Solo me pregunto cómo puedo resolver esto o si hay una forma diferente de resolver este problema.
No funciona porque la representación reacciona de forma síncrona mientras que su getUrl es una función asíncrona. Como señalaron otras respuestas, puede obtener todas las URL en el componentDidMount o useEffect hook y actualizar el estado.
Si no desea cambiar su código existente, también puede reemplazar img con un componente de reacción propio para que pueda manejar la promesa.
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 } Es posible que esto no sea lo mejor para el rendimiento, ya que getUrl enviará una nueva referencia de promesa en cada procesamiento. En este caso, lo mejor sería pasar la id (identificación de usuario en getUrl ) al componente Image en lugar de una promesa y tener la lógica de obtener la URL real dentro de ella.
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> ))}No puede asignar el valor de retorno de getUrl() al atributo src porque es asíncrono y devuelve una Promise .
En su lugar, lo que puede hacer es mantener otro estado para los avatares de los usuarios que se actualice cuando lo hagan sus data . Por ejemplo, cree una función que resuelva los avatares de los usuarios
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 ]) ) ) }Luego en tu componente...
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 changesy en tu 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> ))}Véase también Promise.allSettled()
No es una buena idea obtener el avatar de cada usuario por separado y enviar una solicitud para cada publicación y cada vez que se rendericen los elementos de la publicación. Si es posible para usted y la API de back-end, simplemente incluya la URL del avatar de los usuarios en la publicación para renderizarla fácilmente. si no es posible y tiene una limitación en api, solo necesita obtener todos los datos de usuario de la publicación con Promiss.allSettled en useEffect después de que se hayan recibido los datos de la publicación y adjunte los datos del usuario a postItem o manténgalos en un diccionario separado y luego configure el estado para representar el publicación y url de avatar de usuario en él.