我应该在哪里更新状态以从 API 获得正确的读数?
Where should I update state to get the correct readout from the API?
我有一个大项目要完成我作为开发人员的第一份工作的面试,所以我 a) 没有经验并且 b) 没有时间进行任何剧烈的重构(例如使用 Hooks)。
我的任务是从 API 中提取数据并生成一些图表。我(终于)弄清楚了如何使用 axios 获取我需要的数据,但是在尝试访问该对象时遇到了一个非常奇怪的问题。
父级 <App />
将对象设置为如下状态:
onDateChange = async date => {
const response = await fastapi.get(`/${date}`)
this.setState({apiData:response.data})
}
当我记录状态时,我看到的正是我所期望的。所以我转到子组件 (<DataDisplay />
),我需要将对象拆分成几个变量,这就是事情开始变得混乱的地方。我尝试console.log
this.props.apiData.site_hours
处的数据,没有问题。我取回了一个包含两个键值对的小对象:{"inside": 3.19765, "outside": 4.64378}
。但是当我尝试记录 this.props.apiData.site_hours.inside
时,它告诉我里面是未定义的!
我问了一个朋友,他是一个非常有经验的程序员,虽然不熟悉 React,他说很可能在我记录它的时候它只是未定义的。很奇怪,因为我可以在对象中获取上一层的信息,但是当我向下挖掘一层时,什么都没有!所以我试着移动我的 console.log
,果然,当我把它放在 componentDidUpdate()
中时,site_hours.inside
存在!所以我尝试使用 componentDidUpdate()
来设置状态,但机器立即告诉我这是一个非常非常糟糕的主意。
很明显,这是生命周期和我尝试做事的顺序的问题,但 none 我的研究发现了任何有用的东西!我发现令人难以置信的是,我对该对象的调用将 return 仅提供部分信息,而不是全部信息。请帮忙!
https://github.com/erasebegin/react-projects/tree/master/tharsus-interface/src
已更新 => 试试这个,看看是否可以从 child 组件中记录道具:
import React from "react";
import MovementDataDisplay from "./MovementDataDisplay";
import OverviewDataDisplay from "./OverviewDataDisplay";
import DistributionDataDisplay from "./DistributionDataDisplay";
class DataDisplay extends React.Component {
constructor(props) {
super(props);
}
render() {
const { passAPIData } = this.props
const {siteHours, movingHours} = passAPIData
console.log(siteHours, movingHours)
if (this.props.NavState === "movement") {
return (
<div className="chart-container">
<MovementDataDisplay
Data={passAPIData}
Title="Site Hours"
Labels={['inside','outside']}
/>
<MovementDataDisplay
Data={[1,3]}
Title="Moving Hours"
/>
</div>
);
} else if (this.props.NavState === "overview") {
return (
<div className="chart-container">
<OverviewDataDisplay
Data={[3.3176110809420893, 4.18238891905791]}
Title="Site Hours"
/>
<OverviewDataDisplay
Data={[3.9176110809420893, 4.18238891905791]}
Title="Moving Hours"
/>
</div>
);
} else if (this.props.NavState === "distribution") {
return <DistributionDataDisplay />;
}
}
}
export default DataDisplay;
没有拉回购并调试它,或者 运行 它(诚然)我的猜测是当你在 componentDidMount
中设置状态时你正在导致 re-render child 组件并丢失该值(只是一个猜测) - 在这里我已经删除了 state
一起(我看不出它是如何需要的)和 componentDidMount
并且正在记录你的值直接从道具中寻找。如果这些值设置为 parent 状态并作为道具传递,我没有任何理由(从你目前拥有的)看到你需要 re-set 他们在 child。到目前为止,除了为它设置道具外,你似乎甚至没有在 child 中使用状态,除非这些值将从这个 child 组件中本地更改......你可以(也应该)从 parent 的道具中读取它们 是 定义、更新和传递的。
再次更新(在摆弄实际代码之后;))
检查一下,看看它是否能解决您的问题。我为我所做的小改动添加了评论:
App.js:
import React from "react";
import Nav from "./components/Nav";
import DataDisplay from "./components/DataDisplay";
import "./styles/App.css";
import fastapi from './api/fastapi'
class App extends React.Component {
constructor() {
super();
this.state = {
userDate: "",
apiData: {}, // this was set to a [] but it's data type is a actually an {}
navState: "overview"
};
this.setNavState = this.setNavState.bind(this);
}
setNavState(nav) {
// when triggering this from the UI it causes a state change, and a re-render
const btnArr = ["overview", "movement", "distribution"];
this.setState({ navState: btnArr[nav] });
// this needs to be reset
this.onDateChange(this.state.userDate)
}
onDateChange = async date => {
const response = await fastapi.get(`/${date}`)
console.log('onDateChange response.data', response.data)
this.setState({
apiData: response.data,
userDate: date ? date : this.state.userDate // you were not setting this but you will need it for when setNavState runs
})
}
render() {
console.log("new state is ",this.state.apiData) // you should see this
return (
<div className="App">
<Nav getNav={this.setNavState} getDate={this.onDateChange} />
<DataDisplay NavState={this.state.navState} apiData={this.state.apiData}/>
</div>
);
}
}
export default App;
DateDisplay.js
import React from "react";
import MovementDataDisplay from "./MovementDataDisplay";
import OverviewDataDisplay from "./OverviewDataDisplay";
import DistributionDataDisplay from "./DistributionDataDisplay";
class DataDisplay extends React.Component {
constructor(props) {
super(props);
}
render() {
console.log("from DataDisplay: ", this.props.apiData)
const { apiData : { site_hours = {} }} = this.props
const {inside = 0, outside = 0} = site_hours // defaults to avoid breaking if there is no data here (prior to selecting date for ex)
console.log(inside, outside) // i can see these logs in the child :)
if (this.props.NavState === "movement") {
return (
<div className="chart-container">
<MovementDataDisplay
Data={[inside, outside]} // you are putting the entire 'this.state' here (a big object), but it seems to want a simple array
Title="Site Hours"
Labels={['inside','outside']}
/>
<MovementDataDisplay
Data={[1,3]}
Title="Moving Hours"
/>
</div>
);
} else if (this.props.NavState === "overview") {
return (
<div className="chart-container">
<OverviewDataDisplay
Data={[3.3176110809420893, 4.18238891905791]}
Title="Site Hours"
/>
<OverviewDataDisplay
Data={[3.9176110809420893, 4.18238891905791]}
Title="Moving Hours"
/>
</div>
);
} else if (this.props.NavState === "distribution") {
return <DistributionDataDisplay />; // you need to pass this some data of course :)
}
}
}
export default DataDisplay;
我可以在 child 中看到日志,不需要状态。 我看到 和 IDK 的问题,如果这就是您所说的,是在 Overview/Movement/Distribution 图表之间单击时分页。这是因为您的 api 响应存储到 parent 组件中的本地状态,当您通过在 Nav 中调用 setNavState
更新状态时,您会在其中触发 re-render parent 并且状态丢失,您需要重新获取数据 - 分页符是因为该数据不再可用 child 并且没有适当的保护措施来处理没有数据(这是您应该始终编码以备记录的内容)所以我
1) 保存状态中的日期
2) 和 re-fetch 它在同一个导航回调中(这可能有效,因为 setState 是异步的)
3) 向 child 添加了一些默认值以防止破坏
如果此应用程序具有全局状态,您可以将 apiResponse 存储在那里而不需要重新获取,或者您可以将其存储在本地存储或什至 cookie 中以避免重新获取。全局状态更可取。
我有一个大项目要完成我作为开发人员的第一份工作的面试,所以我 a) 没有经验并且 b) 没有时间进行任何剧烈的重构(例如使用 Hooks)。
我的任务是从 API 中提取数据并生成一些图表。我(终于)弄清楚了如何使用 axios 获取我需要的数据,但是在尝试访问该对象时遇到了一个非常奇怪的问题。
父级 <App />
将对象设置为如下状态:
onDateChange = async date => {
const response = await fastapi.get(`/${date}`)
this.setState({apiData:response.data})
}
当我记录状态时,我看到的正是我所期望的。所以我转到子组件 (<DataDisplay />
),我需要将对象拆分成几个变量,这就是事情开始变得混乱的地方。我尝试console.log
this.props.apiData.site_hours
处的数据,没有问题。我取回了一个包含两个键值对的小对象:{"inside": 3.19765, "outside": 4.64378}
。但是当我尝试记录 this.props.apiData.site_hours.inside
时,它告诉我里面是未定义的!
我问了一个朋友,他是一个非常有经验的程序员,虽然不熟悉 React,他说很可能在我记录它的时候它只是未定义的。很奇怪,因为我可以在对象中获取上一层的信息,但是当我向下挖掘一层时,什么都没有!所以我试着移动我的 console.log
,果然,当我把它放在 componentDidUpdate()
中时,site_hours.inside
存在!所以我尝试使用 componentDidUpdate()
来设置状态,但机器立即告诉我这是一个非常非常糟糕的主意。
很明显,这是生命周期和我尝试做事的顺序的问题,但 none 我的研究发现了任何有用的东西!我发现令人难以置信的是,我对该对象的调用将 return 仅提供部分信息,而不是全部信息。请帮忙!
https://github.com/erasebegin/react-projects/tree/master/tharsus-interface/src
已更新 => 试试这个,看看是否可以从 child 组件中记录道具:
import React from "react";
import MovementDataDisplay from "./MovementDataDisplay";
import OverviewDataDisplay from "./OverviewDataDisplay";
import DistributionDataDisplay from "./DistributionDataDisplay";
class DataDisplay extends React.Component {
constructor(props) {
super(props);
}
render() {
const { passAPIData } = this.props
const {siteHours, movingHours} = passAPIData
console.log(siteHours, movingHours)
if (this.props.NavState === "movement") {
return (
<div className="chart-container">
<MovementDataDisplay
Data={passAPIData}
Title="Site Hours"
Labels={['inside','outside']}
/>
<MovementDataDisplay
Data={[1,3]}
Title="Moving Hours"
/>
</div>
);
} else if (this.props.NavState === "overview") {
return (
<div className="chart-container">
<OverviewDataDisplay
Data={[3.3176110809420893, 4.18238891905791]}
Title="Site Hours"
/>
<OverviewDataDisplay
Data={[3.9176110809420893, 4.18238891905791]}
Title="Moving Hours"
/>
</div>
);
} else if (this.props.NavState === "distribution") {
return <DistributionDataDisplay />;
}
}
}
export default DataDisplay;
没有拉回购并调试它,或者 运行 它(诚然)我的猜测是当你在 componentDidMount
中设置状态时你正在导致 re-render child 组件并丢失该值(只是一个猜测) - 在这里我已经删除了 state
一起(我看不出它是如何需要的)和 componentDidMount
并且正在记录你的值直接从道具中寻找。如果这些值设置为 parent 状态并作为道具传递,我没有任何理由(从你目前拥有的)看到你需要 re-set 他们在 child。到目前为止,除了为它设置道具外,你似乎甚至没有在 child 中使用状态,除非这些值将从这个 child 组件中本地更改......你可以(也应该)从 parent 的道具中读取它们 是 定义、更新和传递的。
再次更新(在摆弄实际代码之后;))
检查一下,看看它是否能解决您的问题。我为我所做的小改动添加了评论:
App.js:
import React from "react";
import Nav from "./components/Nav";
import DataDisplay from "./components/DataDisplay";
import "./styles/App.css";
import fastapi from './api/fastapi'
class App extends React.Component {
constructor() {
super();
this.state = {
userDate: "",
apiData: {}, // this was set to a [] but it's data type is a actually an {}
navState: "overview"
};
this.setNavState = this.setNavState.bind(this);
}
setNavState(nav) {
// when triggering this from the UI it causes a state change, and a re-render
const btnArr = ["overview", "movement", "distribution"];
this.setState({ navState: btnArr[nav] });
// this needs to be reset
this.onDateChange(this.state.userDate)
}
onDateChange = async date => {
const response = await fastapi.get(`/${date}`)
console.log('onDateChange response.data', response.data)
this.setState({
apiData: response.data,
userDate: date ? date : this.state.userDate // you were not setting this but you will need it for when setNavState runs
})
}
render() {
console.log("new state is ",this.state.apiData) // you should see this
return (
<div className="App">
<Nav getNav={this.setNavState} getDate={this.onDateChange} />
<DataDisplay NavState={this.state.navState} apiData={this.state.apiData}/>
</div>
);
}
}
export default App;
DateDisplay.js
import React from "react";
import MovementDataDisplay from "./MovementDataDisplay";
import OverviewDataDisplay from "./OverviewDataDisplay";
import DistributionDataDisplay from "./DistributionDataDisplay";
class DataDisplay extends React.Component {
constructor(props) {
super(props);
}
render() {
console.log("from DataDisplay: ", this.props.apiData)
const { apiData : { site_hours = {} }} = this.props
const {inside = 0, outside = 0} = site_hours // defaults to avoid breaking if there is no data here (prior to selecting date for ex)
console.log(inside, outside) // i can see these logs in the child :)
if (this.props.NavState === "movement") {
return (
<div className="chart-container">
<MovementDataDisplay
Data={[inside, outside]} // you are putting the entire 'this.state' here (a big object), but it seems to want a simple array
Title="Site Hours"
Labels={['inside','outside']}
/>
<MovementDataDisplay
Data={[1,3]}
Title="Moving Hours"
/>
</div>
);
} else if (this.props.NavState === "overview") {
return (
<div className="chart-container">
<OverviewDataDisplay
Data={[3.3176110809420893, 4.18238891905791]}
Title="Site Hours"
/>
<OverviewDataDisplay
Data={[3.9176110809420893, 4.18238891905791]}
Title="Moving Hours"
/>
</div>
);
} else if (this.props.NavState === "distribution") {
return <DistributionDataDisplay />; // you need to pass this some data of course :)
}
}
}
export default DataDisplay;
我可以在 child 中看到日志,不需要状态。 我看到 和 IDK 的问题,如果这就是您所说的,是在 Overview/Movement/Distribution 图表之间单击时分页。这是因为您的 api 响应存储到 parent 组件中的本地状态,当您通过在 Nav 中调用 setNavState
更新状态时,您会在其中触发 re-render parent 并且状态丢失,您需要重新获取数据 - 分页符是因为该数据不再可用 child 并且没有适当的保护措施来处理没有数据(这是您应该始终编码以备记录的内容)所以我
1) 保存状态中的日期 2) 和 re-fetch 它在同一个导航回调中(这可能有效,因为 setState 是异步的) 3) 向 child 添加了一些默认值以防止破坏
如果此应用程序具有全局状态,您可以将 apiResponse 存储在那里而不需要重新获取,或者您可以将其存储在本地存储或什至 cookie 中以避免重新获取。全局状态更可取。