函数组件接收道具但不渲染

时间:2020-09-09 14:50:53

标签: reactjs async-await react-hooks react-props

情况:

首先,我从数据库中获取 imgs 列表:

{imgs:
    [
        {_id: '...',img:'***.png'},
        ...,
    ]
}

然后使用 ali-oss-hook 签名 img.src ,结果如下:

{imgs:
    [
        {_id:'...', img: '***.png', src: 'signatured-http-address'}
        ...,
    ]
}

然后,将imgs传递给 PictureList 组件:

<PictureList imgs={images}

PictureList 收到了新的道具,但没有渲染

const PictureList = ({ imgs }) => {
 return (
 <ul>
 {imgs.map((i) => (
   <img key={i._id} src={i.src} alt="pic" />
 ))}
 </ul>
 );
}
export default PictureList

代码

Picture.js

import React, {useEffect, useState, useRef } from 'react'
import { useAlioss } from '../../hooks/oss-hook'
import PictureList from '../../components/PictureList'

import './style.less'

const Pictures = () => {
  const [loading, setLoading] = useState(true)
  const [signatured, setSignatured] = useState(false)
  const [results, setResults] = useState()
  const [images, setImages] = useState([])

  const { allowUrl } = useAlioss()
 
  const resultsDoSetRef = useRef(false)
  
  async function getImages() {
    try {
      const dbResponse = await fetch(
        `${process.env.REACT_APP_BACKEND_URL}/upload/images`
      );

      const resu = await dbResponse.json();

      setResults(resu)
      resultsDoSetRef.current = true

    } catch (e) {
      console.log("get images failed")
    } finally {
      setLoading(false)
      console.log("get images done!")
    }
  }

  useEffect(() => {
    getImages();
  }, [])
  
  async function signatureUrl(raw) {
    setSignatured(false)
    try {
      let tempImgs = []

      raw.imgs.forEach((r) => {
        allowUrl(r.img).then((res) => {
          r.img = res;
          tempImgs.push(r)
        });
      });

      setImages(tempImgs);
    } catch (e) {
      console.log("signature failed",e)
    } finally {
      setSignatured(true)
      console.log("signature done!")
    }
  }

  useEffect(() => {
    if (resultsDoSetRef.current) {
      resultsDoSetRef.current = false
      signatureUrl(results);
    }
  },[results])
  
  return (
    <div className="picture">
      {loading ? <h1>Loading</h1> : <PictureList imgs={images} />}
    </div>
  );
};

export default Pictures

PictureList.js

const PictureList = ({ imgs }) => {
 return (
 <ul>
 {imgs.map((i) => (
   <img key={i._id} src={i.src} alt="pic" />
 ))}
 </ul>
 );
}
export default PictureList

chrome react devTool component shows props

chrome devTool element shows empty PictureList

chrome devTool 反应组件中显示了正确的道具,但是PictureList组件仍然为空<ul></ul>

哪一部分错了?

2 个答案:

答案 0 :(得分:0)

看看PictureList.js,您将“ imgs”作为该函数的参数,这与您传入的属性不同

<PictureList imgs={images}

此“ imgs”实际上是具有imgs属性的对象,因此您的代码将变为:

const PictureList = ({ imgs }) => {
 return (
 <ul>
 {imgs.imgs.map((i) => (
   <img key={i._id} src={i.src} alt="pic" />
 ))}
 </ul>
 );
}
export default PictureList

P.S:只是一个建议,通常使用props(或类似描述的东西)作为参数自变量,因此您的代码将如下所示:

const PictureList = ({ props }) => {
     return (
     <ul>
     {props.imgs.map((i) => (
       <img key={i._id} src={i.src} alt="pic" />
     ))}
     </ul>
     );
    }
    export default PictureList

答案 1 :(得分:0)

Picture --signatureUrl()方法中,raw.imgs.forEach()返回承诺的束缚,这些承诺无法一次全部解决。

setImages(tempImgs)时, useState挂钩中的图像首先接收到一个空数组,然后在出现以下情况时将新图像推入 images 数组raw.imgs.forEach()返回了承诺解决新的图片项

因此,我们必须等待所有allowUrl(r.img)的承诺都得到解决,然后setImages(tempImgs)

function signatureUrl(raw) {
    const tasks = raw.imgs.map(i => allowUrl(i.img))
    Promise.all(tasks).then(values => {
      let resultImgs = raw.imgs.map((t, index) => ({ ...t, src: values[index] }));

      setImages(resultImgs)
    })
  }

PS:解决方案确实可以,但是所有分析可能都错了,仅供参考。