繁体   English   中英

ReactJs在状态改变时重新渲染

[英]ReactJs re-render on state change

我正在尝试执行以下操作以获取一些API数据并按如下方式进行操作:

  1. 通过API获取字符串地址列表
  2. 通过API将每个字符串地址转换为地理编码的位置
  3. 在地图上将这些经过地理编码的地址显示为标记

我在正确安排所有这些异步活动的时间方面遇到了一些麻烦(对于Javascript我是很陌生的)。

这是我到目前为止的内容:

class Map extends Component {
    constructor(props) {
        super(props);

        this.state = {
            addresses = [],
            geocodedAddresses = []
        }
    }

    componentDidMount() {
        const geocodedAddresses = []
        fetch('.....')
            .then(result => result.json())
            .then(addresses => this.setState({ addresses }, function() {
                this.state.addresses.forEach(address => {
                    Geocode.fromAddress(address).then(geocodedAddress => {
                        geocodedAddresses.push(geocodedAddress['results'][0]['geometry']['location'])
                    })
                })
            }))
            console.log(geocodedAddresses) //Correctly contains the geocoded addresses
            this.setState({ geocodedAddresses })
        }
    }

    render() {
        this.state.addresses.map(a => console.log(a)) //Evaluates correctly
        this.state.geocodedAddresses.map(ga => console.log(ga)) //Yields nothing....
    .....
    }
}

所以我不太明白为什么我这样做时React不会重新渲染this.setState({ geocodedAddresses }) -当我执行setState时不应该重新渲染吗?

您的代码有几个错误。 首先,使用equals而不是冒号创建状态对象:

this.state = {
    addresses: [],
    geocodedAddresses: []
}

其次,您应考虑到代码是异步的。 只有当通过调用产生的诺言Geocode.fromAddress解决您为您的数据geocodedAddresses

componentDidMount ,您正在控制台记录geocodedAdresses并报告看到了正确的值。 这仅是因为在承诺解决之后,日志已更新。 但是,当你做console.log在那一刻的价值geocodedAdresses是一个空数组。 这就是在组件state插入的值。

为了为状态设置正确的值,您应该在所有Geocode.fromAddress承诺均已解决后调用setState 为此,您可以使用Promise.all方法。

因此,您的代码如下所示:

class Map extends Component {
    constructor(props) {
        super(props);

        this.state = {
            addresses: [],
            geocodedAddresses: []
        }
    }

    componentDidMount() {
        fetch('.....')
            .then(result => result.json())
            .then(addresses => {
                Promise.all(addresses.map(address => Geocode.fromAddress(address)))
                    .then(geocodedAddresses => {
                        this.setState({
                            addresses,
                            geocodedAddresses
                        })
                    });
            }))
        }
    }

    render() {
        this.state.addresses.map(a => console.log(a)) //Evaluates correctly
        this.state.geocodedAddresses.map(ga => console.log(ga)) //Yields nothing....
    .....
    }
}

请注意,使用此解决方案, setState仅被调用一次。

由于您所有的state涉及到地址信息,因此有可能将该信息合并到state的单个键中。 您可以在构造函数中将state初始化为:

this.state = {
    addresses: []
};

然后,一旦所有承诺都解决,您就可以填充state

componentDidMount() {
    fetch('.....')
        .then(result => result.json())
        .then(addresses => {
            Promise.all(addresses.map(address => Geocode.fromAddress(address)))
                .then(geocodedAddresses => {
                    const addressesState = addresses.map((address, i) => {
                        return {
                            address,
                            geocodedAddress: geocodedAddresses[i]
                        };
                    });
                    this.setState({ addresses: addressesState })
                });
        }))
    }
}

您说对了,这是异步稍微关闭了。 console.log它“出现”在控制台中填充,但是当setState被调用时(以及console.log被调用时),它的值仍然是[]

如果您使用的是async/await ,则可以等待fetch链完全完成,或者将setState放在then 使用setState回调,最好执行后者。

componentDidMount() {
  const geocodedAddresses = []
  fetch('.....')
  .then(result => result.json())
    .then(addresses => this.setState({ addresses }, function() {
      this.state.addresses.forEach(address => {
        Geocode.fromAddress(address).then(geocodedAddress => {
          geocodedAddresses.push(geocodedAddress['results'][0]['geometry']['location'])
        })
      })
   console.log(geocodedAddresses) //Correctly contains the geocoded addresses
   this.setState({ geocodedAddresses })
}))

}}

要么

componentDidMount = async () => {
      const geocodedAddresses = []
      let addresses = await fetch('.....').then(result => result.json())
      addresses.forEach(address => { // assuming this is sync
            Geocode.fromAddress(address).then(geocodedAddress => {
              geocodedAddresses.push(geocodedAddress['results'][0]['geometry']['location'])
            })
          })
       this.setState({ geocodedAddresses,addresses })

    }}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM