【问题标题】:ReactJS is calling the render method before it receives the data to renderReactJS 在接收到要渲染的数据之前调用渲染方法
【发布时间】:2020-11-11 06:23:27
【问题描述】:

当主组件加载时,我向我的 API 发送一个 GET 请求,以获取在名为 "ship_name" 的字段中具有特定值的所有对象,这意味着可能会到达更多“构建”,因此会有更多呈现。加载主组件后,我在同一个文件中创建了第二个组件,称为“Builds”。作为道具,我向这个组件发送 this.state.builds,一个对象数组。

更好的解释:

  1. 用户在搜索栏中插入“ship_name”
  2. API 会查找具有该“ship_name”的所有构建
  3. 所有构建都发送到子组件
  4. 使用“gears”(齿轮名称)API 查找文件名并将其发回
  5. !在链接到达之前反应渲染
<Builds builds={this.state.builds} />

“构建”的结构是这样的

[
    {
        "gears": [
            "Triple 410mm (10th Year Type Prototype)",
            "Triple 155mm (3rd Year Type)",
            "Twin 40mm Bofors STAAG Mk II",
            "High Performance Fire Control Radar",
            "Type 1 Armor Piercing Shell"
        ],
        "rarities": [
            "o",
            "o",
            "o",
            "o",
            "o"
        ],
        "_id": "5eac7b450e64096b082f4c43",
        "ship_name": "Gascogne",
        "pv": "e"
    },
    {
        "gears": [
            "Triple 410mm (10th Year Type Prototype)",
            "Triple 155mm (3rd Year Type)",
            "Twin 40mm Bofors STAAG Mk II",
            "High Performance Fire Control Radar",
            "Type 1 Armor Piercing Shell"
        ],
        "rarities": [
            "o",
            "o",
            "o",
            "o",
            "o"
        ],
        "_id": "5f131d6e2e3f16ef3de48e4f",
        "ship_name": "Gascogne",
        "pv": "e"
    }
]

到目前为止一切正常。

render() {
        return (
            this.props.builds.map((build) => (
                <div className="container" key={uuidv4()}>
                    <img src={"/img/gears/" + this.getGearData(build.gears[0])} className={build.rarities[0] + " gI g1"}></img><div className={"t t1"}>{build.gears[0]}</div>
                    <img src={"/img/gears/" + this.getGearData(build.gears[1])} className={build.rarities[1] + " gI g2"}></img><div className={"t t2"}>{build.gears[1]}</div>
                    <img src={"/img/gears/" + this.getGearData(build.gears[2])} className={build.rarities[2] + " gI g3"}></img><div className={"t t3"}>{build.gears[2]}</div>
                    <img src={"/img/gears/" + this.getGearData(build.gears[3])} className={build.rarities[3] + " gI g4"}></img><div className={"t t4"}>{build.gears[3]}</div>
                    <img src={"/img/gears/" + this.getGearData(build.gears[4])} className={build.rarities[4] + " gI g5"}></img><div className={"t t5"}>{build.gears[4]}</div>
                </div>
            ))

        )
    }

/*------------------------------*/

getGearData(gear_name) {
        let requestString = encodeURI('http://localhost:5000/gears/one?q=' + gear_name)
        let data;
        Axios.get(requestString)
            .then(res => {
                console.log(res.data[0].image_link)
                data = res.data[0].image_link
            })

        return data

        /* what */
    }

这里发生的情况是,在渲染的同时,我试图获取图像的 URL。 GET 方法有效,实际上 console.log 打印文件的实际名称

32200.png gear.component.js:76 
26600.png gear.component.js:76 
1260.png gear.component.js:76 
600.png gear.component.js:76 
34180.png gear.component.js:76 

但在 SRC 上,结果是 "/img/gears/undefined"。我无法理解的一件事是为什么所有结果都被打印了 2 次,但这并不是什么大问题,因为最终这些东西只被正确渲染了一次。

我尝试过使用许多方法、状态、Promises(即使我还没有真正理解它们是如何工作的),这是我第一次真正陷入这样的困境。事实上,主要组件使用“ship_name”来获取“ship_icon”和“faction_icon”的文件名并且没有问题,可能是因为我使用了那里的状态。

这里是它现在的样子

这里有一个关于它应该是什么样子的想法

我曾经使用 php 和 SQL 完成了这个个人项目,现在我正在尝试使用 React 和 MongoDB 来完成。

【问题讨论】:

标签: javascript reactjs


【解决方案1】:

这是因为Axios.get 是一个异步 函数,因此您在设置数据之前返回变量。

我还要说这是一个糟糕的设计,例如,将获取异步数据和渲染的关注点分开会更好。

async componentDidMount() {
    const containerToRender = (<></>);

    await Promise.all(
      this.props.builds.map(async (build) => {
        const container = <container ... key={build._id} />
        await Promise.all(build.gears.map(async (gear, i) => {
          container.children.push(<img src={"/img/gears/" + await this.getGearData(gear)} className={`${build.rarities[i]} gI g${i + 1}"} key={`img.${gear}.${i} />)
          container.children.push(<div className={`t t${i + 1}`} key={`div.${gear}.${i}>{gear}</div>)
        })
        containerToRender.children.push(container)
      })

    this.setState({ containerToRender });
}

getGearData(gear_name) {
  const requestString = encodeURI('http://localhost:5000/gears/one?q=' + gear_name)

  return Axios.get(requestString).then(res => res.data[0].image_link)
}

render() {
    return (this.state.containerToRender);
}

【讨论】:

  • await 绝对不会解决问题,它只是承诺的语法糖。您仍然需要找到一种方法来获取渲染过程外部的异步数据
  • 并且将 JSX 节点存储在 state 中是一种反模式。
【解决方案2】:

注意:您应该首先在检索和/或设置构建的过程中调用获取图像 URL,这种数据结构是设计不佳的标志;现在,也就是说:

  1. React 是一个 反应式 库。它只它正在监控的数据的变化做出反应。

  2. 您不能混合使用异步和同步函数并期望一切都等待。

当您致电getGearData(gear_name) 时,这里是正在发生的事情的粗略概述按照发生的顺序

  1. 你用变量gear_name调用getGearData
  2. 声明了一个可修改变量 requestString 并将其设置为 encodeURI('http://localhost:5000/gears/one?q=' + gear_name)(旁注,这应该是 const,因为它没有被修改
  3. 声明了一个可修改的变量data
  4. 异步请求已启动以从服务器检索数据
  5. data当前值的引用返回给调用者
  6. 反应渲染
  7. 您的请求完成
  8. 局部变量data设置在函数范围内

你必须等待异步操作

有很多方法可以做到这一点。这是一个使用占位符图像的示例

const placeholder = "https://link.to/placeholder-image.png"

class MyComponent extends Component {

  constructor(props) {
    super(props)
    this.state = { images: {} }
  }
  componentDidUpdate(prevProps) {
    if(prevProps.builds != this.props.builds) {
      this.fetchImages()
    }
  }

  fetchImages() {
    this.props.builds.forEach(build => {
      build.gears.forEach(gear => {
        Axios.get(encodeURI('http://localhost:5000/gears/one?q=' + gear_name))
          .then(res => {
            const link = res.data[0].image_link 
            console.log(link)
            this.setState(state => { images: { ...state.images, [gear]: link } })
          })
      })
    })
  }

  mapGears = (gears) =>
    gears.map(
      (gear, i) =>
        <img
          src={this.state.images[gear] || placeholder}
          className={`t t${i}`}
          key={`${gear}.${i}`}
        />
    )

  render() {
    return (
      this.props.builds.map((build) => (
        <div className="container" key={build._id}>
          {this.mapGears(build.gears)}
        </div>
      ))
    }
}

【讨论】:

  • 非常感谢!我改变了一些小东西,现在效果很好,这就是它的外观now。在我继续之前,我最好学习一下 promises 和异步 JS,我可以清楚地看到我的代码是什么,什么不是。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-08
  • 2019-05-11
  • 1970-01-01
  • 1970-01-01
  • 2017-11-11
相关资源
最近更新 更多