与生命周期方法混淆 - 为什么我的 API 调用没有返回数据?
Confusion with lifecycle methods - why is my API call not returning data?
我正在使用 React 构建一个酒吧网站,这需要我获取有关桶装啤酒的信息。我正在从 http://www.hashigozake.co.nz/taplist.xml
中检索该信息,如您所见,数据的格式为 return XML。然后使用名为 xml-react-parser.
的库将此数据转换为 js 对象
无论如何,我编写了以下代码来进行此 API 调用:
componentDidMount() {
const url = 'http://www.hashigozake.co.nz/taplist.xml'
const proxyURL = 'https://cors-anywhere.herokuapp.com/'
fetch(proxyURL + url)
.then(res => res.text())
.then(beerXML => {
let beerJs = new XMLParser().parseFromString(beerXML)
this.setState({
beers: beerJs
})
})
}
这就是问题所在。我将 taplist 数据 ({beerList =this.state.beers}
) 作为道具传递给另一个名为 TileList.jsx
的组件。在这个组件中,我 运行 以下控制台日志:
console.log(props.beerList.children[0])
应该 return 一个对象数组,但是却给我一条错误消息,说 Uncaught TypeError: Cannot read property '0' of undefined
.
我怀疑正在发生的事情是在 fetch() 方法完成 returning 数据之前调用了 render() 方法,这就是为什么我得到一个 undefined
当我尝试访问我的数据。我很困惑,因为我认为 componenentDidMount 是在 render 方法之后触发的,所以我的数据不应该被渲染吗?
无论如何,这里更详细地看一下我的代码:
父组件:Home.jsx
import React from 'react'
import './styles.css'
import hashi_logo_2 from './Photos/hashi_logo_2.png'
import TileList from './TileList'
import OnNextList from './OnNextList'
import {Link} from 'react-router-dom'
const XMLParser = require('react-xml-parser')
class Home extends React.Component {
constructor() {
super()
this.state = {
beers:[]
}
}
componentDidMount() {
const url = 'http://www.hashigozake.co.nz/taplist.xml'
const proxyURL = 'https://cors-anywhere.herokuapp.com/'
fetch(proxyURL + url)
.then(res => res.text())
.then(beerXML => {
let beerJs = new XMLParser().parseFromString(beerXML)
this.setState({
beers: beerJs
})
})
}
render() {
return (
<div className = 'home'>
<nav className = 'nav'>
<ul>
<Link to = '/about' style={{ textDecoration: 'none' }} >
<li className='list'>About</li>
</Link>
<li className='list'>Food</li>
<li className='list'>Events</li>
<li className='list'>News</li>
<Link to ='/shop' style={{ textDecoration: 'none' }} >
<li className='list'>Shop</li>
</Link>
<li className='list'>Contact</li>
<li className='list'>Blog</li>
</ul>
</nav>
<main>
<div className='main__image'>
<img src= {hashi_logo_2}/>
</div>
<div>
<TileList beerList = {this.state.beers}/>
<OnNextList/>
</div>
</main>
</div>
)
}
}
export default Home
子组件:TileList.jsx
import React from 'react'
import Tile from './Tile'
const TileList = (props) => {
console.log(props.beerList.children[0])
return (
<>
<h1 className = 'h1-pouring'>Currently Pouring:</h1>
<div className = 'tile-container'>
{
props.beerList.map(item => {
return <Tile
/>
})
}
</div>
</>
)
}
export default TileList
此外,这是作为 props 发送的数据的(不完整)视图:
{
name: 'Products', attributes:{}, value:'', children: [
{
name:'Beers', attributes: {}, value: '', children: [
{
name: 'Product', attributes:{}, value:'', children: {
}
}
]
}
]
}
根据react文档here,setState是异步的,在第一次渲染中,props.beerList是一个空数组,然后setState()会触发第二次渲染,所以你可以通过如果您的 return 包含一个值,则第二个渲染中的索引。
setState() does not always immediately update the component. It may
batch or defer the update until later. This makes reading this.state
right after calling setState() a potential pitfall. Instead, use
componentDidUpdate or a setState callback (setState(updater,
callback)), either of which are guaranteed to fire after the update
has been applied.
问题
您的初始状态没有提供足够的数据结构,无法更深入地正确访问。
beers
对象最初在 Home
中定义为一个空数组
this.state = {
beers: [],
}
并作为 prop 传递给 TileList
<TileList beerList={this.state.beers} /> // this.state.beers ~ []
在 TileList
内,您尝试立即更深入地记录
console.log(props.beerList.children[0]);
检查一下,props.beerList
当前是空数组 ([]
),没有 children
的 属性,即 props.beerList.children
或 props.beerList['children']
未定义,因此尝试访问索引 0
将引发错误 Uncaught TypeError: Cannot read property '0' of undefined
.
次要问题
初始状态为beers = []
,但后来响应形状为对象
{
name: 'Products', attributes:{}, value:'', children: [
{
name:'Beers', attributes: {}, value: '', children: [
{
name: 'Product', attributes:{}, value:'', children: {
}
}
]
}
]
}
这种不一致的状态类型会使调试代码变得更加困难,或者换句话说,更容易出现错误代码。
解决方案
保持状态类型一致。
this.state = {
beers: {},
}
首先使用守卫或可选链接进行检查
props.beerList.children &&
props.beerList.children.length &&
console.log(props.beerList.children[0]);
或
console.log(props.beerList?.children?.[0]);
访问正确的嵌套 属性 以映射啤酒
props.beerList.children &&
props.beerList.children.length &&
props.beerList.children[0].children &&
props.beerList.children[0].children.map(product => ...
我正在使用 React 构建一个酒吧网站,这需要我获取有关桶装啤酒的信息。我正在从 http://www.hashigozake.co.nz/taplist.xml
中检索该信息,如您所见,数据的格式为 return XML。然后使用名为 xml-react-parser.
无论如何,我编写了以下代码来进行此 API 调用:
componentDidMount() {
const url = 'http://www.hashigozake.co.nz/taplist.xml'
const proxyURL = 'https://cors-anywhere.herokuapp.com/'
fetch(proxyURL + url)
.then(res => res.text())
.then(beerXML => {
let beerJs = new XMLParser().parseFromString(beerXML)
this.setState({
beers: beerJs
})
})
}
这就是问题所在。我将 taplist 数据 ({beerList =this.state.beers}
) 作为道具传递给另一个名为 TileList.jsx
的组件。在这个组件中,我 运行 以下控制台日志:
console.log(props.beerList.children[0])
应该 return 一个对象数组,但是却给我一条错误消息,说 Uncaught TypeError: Cannot read property '0' of undefined
.
我怀疑正在发生的事情是在 fetch() 方法完成 returning 数据之前调用了 render() 方法,这就是为什么我得到一个 undefined
当我尝试访问我的数据。我很困惑,因为我认为 componenentDidMount 是在 render 方法之后触发的,所以我的数据不应该被渲染吗?
无论如何,这里更详细地看一下我的代码:
父组件:Home.jsx
import React from 'react'
import './styles.css'
import hashi_logo_2 from './Photos/hashi_logo_2.png'
import TileList from './TileList'
import OnNextList from './OnNextList'
import {Link} from 'react-router-dom'
const XMLParser = require('react-xml-parser')
class Home extends React.Component {
constructor() {
super()
this.state = {
beers:[]
}
}
componentDidMount() {
const url = 'http://www.hashigozake.co.nz/taplist.xml'
const proxyURL = 'https://cors-anywhere.herokuapp.com/'
fetch(proxyURL + url)
.then(res => res.text())
.then(beerXML => {
let beerJs = new XMLParser().parseFromString(beerXML)
this.setState({
beers: beerJs
})
})
}
render() {
return (
<div className = 'home'>
<nav className = 'nav'>
<ul>
<Link to = '/about' style={{ textDecoration: 'none' }} >
<li className='list'>About</li>
</Link>
<li className='list'>Food</li>
<li className='list'>Events</li>
<li className='list'>News</li>
<Link to ='/shop' style={{ textDecoration: 'none' }} >
<li className='list'>Shop</li>
</Link>
<li className='list'>Contact</li>
<li className='list'>Blog</li>
</ul>
</nav>
<main>
<div className='main__image'>
<img src= {hashi_logo_2}/>
</div>
<div>
<TileList beerList = {this.state.beers}/>
<OnNextList/>
</div>
</main>
</div>
)
}
}
export default Home
子组件:TileList.jsx
import React from 'react'
import Tile from './Tile'
const TileList = (props) => {
console.log(props.beerList.children[0])
return (
<>
<h1 className = 'h1-pouring'>Currently Pouring:</h1>
<div className = 'tile-container'>
{
props.beerList.map(item => {
return <Tile
/>
})
}
</div>
</>
)
}
export default TileList
此外,这是作为 props 发送的数据的(不完整)视图:
{
name: 'Products', attributes:{}, value:'', children: [
{
name:'Beers', attributes: {}, value: '', children: [
{
name: 'Product', attributes:{}, value:'', children: {
}
}
]
}
]
}
根据react文档here,setState是异步的,在第一次渲染中,props.beerList是一个空数组,然后setState()会触发第二次渲染,所以你可以通过如果您的 return 包含一个值,则第二个渲染中的索引。
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied.
问题
您的初始状态没有提供足够的数据结构,无法更深入地正确访问。
beers
对象最初在 Home
this.state = {
beers: [],
}
并作为 prop 传递给 TileList
<TileList beerList={this.state.beers} /> // this.state.beers ~ []
在 TileList
内,您尝试立即更深入地记录
console.log(props.beerList.children[0]);
检查一下,props.beerList
当前是空数组 ([]
),没有 children
的 属性,即 props.beerList.children
或 props.beerList['children']
未定义,因此尝试访问索引 0
将引发错误 Uncaught TypeError: Cannot read property '0' of undefined
.
次要问题
初始状态为beers = []
,但后来响应形状为对象
{
name: 'Products', attributes:{}, value:'', children: [
{
name:'Beers', attributes: {}, value: '', children: [
{
name: 'Product', attributes:{}, value:'', children: {
}
}
]
}
]
}
这种不一致的状态类型会使调试代码变得更加困难,或者换句话说,更容易出现错误代码。
解决方案
保持状态类型一致。
this.state = {
beers: {},
}
首先使用守卫或可选链接进行检查
props.beerList.children &&
props.beerList.children.length &&
console.log(props.beerList.children[0]);
或
console.log(props.beerList?.children?.[0]);
访问正确的嵌套 属性 以映射啤酒
props.beerList.children &&
props.beerList.children.length &&
props.beerList.children[0].children &&
props.beerList.children[0].children.map(product => ...