在事件监听器之后对组件进行渲染

问题描述

我在获取组件生命周期以与React中的事件侦听器配合方面遇到一些麻烦。下面是一个示例应用程序,该应用程序显示用户提供的URL中的图像,并使用图像的宽度来应用样式-在这种情况下,我希望图像以1/2比例显示,这样我就可以得到图像的宽度和然后将其宽度设置为实际宽度的1/2。

为此,我将图像的naturalWidth用作render()方法中的属性。我在componentDidMount()方法中创建图像的实例,然后使用事件侦听器等待图像加载完成以获取其宽度。如果没有事件侦听器,它将尝试在加载图像之前获取宽度,并且不会返回任何值,因此侦听器将确保在获取宽度之前加载图像。那就是我遇到的一个问题:render()方法不受事件监听器的限制,因此即使在{em>之前 componentDidMount()触发render(),它也总是以初始状态进行渲染。我希望通过将状态更改为事件的一部分来重新渲染该组件,但事实并非如此。

在事件侦听器完成获取图像宽度值之后,确保内容呈现的“正确”方法是什么?

import React,{Component,} from 'react'
import './app.css'

class App extends Component {

// setting the initial state with a placeholder image
state = {
    src: "https://cdn.jpegmini.com/user/images/slider_puffin_jpegmini_mobile.jpg",width: "",}

// Getting the width of the image when the component mounts
componentDidMount() {
    let Img = document.createElement("img");
    Img.src = this.state.src
    Img.addEventListener("load",() => {
        this.state.width = Img.naturalWidth
    });
}

//re-calculating the width when the image changes (exact same as above)
componentDidUpdate() {
    let Img = document.createElement("img");
    Img.src = this.state.src
    Img.addEventListener("load",() => {
        this.state.width = Img.naturalWidth
    });
}

// This function updates the state when the input is changed
change = (e) => {
    this.setState({
  [e.target.name]: e.target.value
    })
};

render() {

    return (
        <div className="app">
                <input
                    name="src"
                    value={this.state.src}
                    onChange={e => this.change(e)}
                    disabled={false}
                    ></input>
                <img
                    src={this.state.src}
                    style={{width: this.state.width / 2}}
                    ></img>
            </div>
    )

}
}

export default App

解决方法

您可以有条件地渲染图像,并且仅在this.state.width保留值的情况下显示图像。

此外,您应该使用setState,而不是更改现有状态(这不是React的工作方式,不会导致状态更新)。

与其重复图像宽度计算两次,不如将其放入一个函数并两次调用该函数。也尽可能使用remember to use const代替let

calcImageWidth() {
    const img = document.createElement("img");
    img.src = this.state.src
    img.addEventListener("load",() => {
        // use this technique in componentDidUpdate too
        this.setState(prevState => ({ ...prevState,width: img.naturalWidth }));
    });
}
componentDidMount() {
  this.calcImageWidth();
}
componentDidUpdate() {
  this.calcImageWidth();
}
render() {
    return (
        <div className="app">
            <input
                name="src"
                value={this.state.src}
                onChange={e => this.change(e)}
                disabled={false}
            ></input>
            {
                this.state.width === ''
                    ? null
                    : <img
                          src={this.state.src}
                          style={{ width: this.state.width / 2 }}
                      ></img>
            }
        </div>
    );
}