【发布时间】:2022-01-19 16:51:28
【问题描述】:
所以我尝试在第一次渲染时获取数据,然后对其进行一些计算并将其显示在表格中。但是在第一次渲染之后 fetchArray 的状态是一个空数组,如果我点击更新表按钮,它只会获取第一部分,即用户订单:After first render
第一次渲染后,当我单击更新表按钮时,它再次渲染,我只从 DB fetch 中获取数据: After update table click
现在,在第三次点击之后,一切终于呈现出来了:After 2nd click
这些是正在使用的文件:
OrderList.js
export default function OrderList() {
const [userInfo, setUserInfo] = useState([]);
const [loading, setLoading] = useState(true);
const [errorMsg, setErrorMsg] = useState("");
const [fetchArray, setFetchArray] = useState([]);
const [state, dispatch] = useContext(Context)
const [authenticatedUser, setAuthenticatedUser] = useState(true)
const orders = []
//get the logged in user from local storage then get user ID
const getAuthUser = () =>{
try{
const userId = localStorage.getItem('user')
const foo = JSON.parse(userId)
const id = foo.auth.user
//console.log(id)
if(!id || id == null){
console.log(userId)
setAuthenticatedUser(false)
}else{
return id
}
}catch(e){
console.log(e)
console.log("No auth")
}
}
const deleteOrder = async(row) =>{
const order = {
ticker: row.ticker,
amount: row.amount,
price: row.price,
date: row.date,
comments: row.comments
}
console.log("Deleted " + JSON.stringify(order))
/*try{
await axios.delete(`http://localhost:8081/api/auth/delete-order/${getAuthUser()}`, order)
.then((res) =>{
console.log("deleted " + JSON.stringify(res))
setIsDelete(true)
}).catch(e =>{
console.log(e)
})
}catch(e){
console.log("inside deleteORder: " + e)
}*/
}
//Calculates the profit or loss for a single position
const profitLossCalculator = (price, currentPrice, quantity) => {
let profitLoss = 0;
if (currentPrice) {
profitLoss = (currentPrice - price) * quantity;
}
return profitLoss.toFixed(2)
}
//Calculates the profit or loss for the whole portfolio
const profitLossTotalCalculator = (fetchArray) => {
let profitLossTotal = 0;
fetchArray.forEach((stock) => {
if (!isNaN(Number(stock.profitLoss))) {
profitLossTotal += Number(stock.profitLoss);
}
});
return profitLossTotal.toFixed(2);
}
const getOrders = ()=>{
setErrorMsg("")
setFetchArray([])
axios.get(`http://localhost:8081/api/auth/user/${getAuthUser()}`)
.then(response => {
if(response){
setUserInfo(response.data)
console.log(userInfo)
}else if(!response.data[orders] < 1){
setErrorMsg("No orders to display. Add orders and try again")
}
}).then((() =>{
if(fetchArray.length > 0){
console.log("jeje")
}
}))
.catch(e =>{
setErrorMsg("Something went wrong while trying to get your orders")
console.log(errorMsg + e)
})
setLoading(false)
const foo = userInfo.orders
for(var i in foo){
const fee = foo[i]
setFetchArray(prev =>[...prev, fee])
}
if(fetchArray.length > 0){
stockFetcher(fetchArray, setFetchArray, profitLossCalculator)
}
}
useEffect(() =>{
getOrders()
}, [])
//if data is being fetched
if(loading) {
return (
<>
<p>Data is loading...</p>
</>)
}
return (
<div>
{authenticatedUser ? (
<div style={{display: "flex", width: "150vh", minHeight: "100vh"}}>
<div style={{display: "inline-block", width: "150vh"}}>
<TableContainer component={Paper} sx={{maxWidth: 1200, padding: 6, margin: "auto", marginTop: 10, marginBottom: 10}}>
<h1 style={{textAlign: "center"}}>Your orders, {userInfo.firstName}</h1>
<div>
<h4>
Total profit: <p style={{color: `${profitLossTotalCalculator(fetchArray) <= 0 ? "red" : "green"}`}}>{profitLossTotalCalculator(fetchArray)}€</p>
</h4>
</div>
<Table sx={{padding: 20, minHeight: 350,}} aria-label="simple table">
<TableHead>
<TableRow sx={{maxHeight: 600}}>
<TableCell></TableCell>
<TableCell>Ticker</TableCell>
<TableCell align="right">Price(€)</TableCell>
<TableCell align="right">Amount</TableCell>
<TableCell align="right">Date</TableCell>
<TableCell align="right">Current price</TableCell>
<TableCell align="right">Profit/Loss</TableCell>
<TableCell align="right">Comments</TableCell>
</TableRow>
</TableHead>
<TableBody>
{fetchArray ? (fetchArray.map((row) => (
<TableRow
key={row.ticker}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell>
<Button onClick={() => deleteOrder(row)}>
<DeleteIcon />
</Button>
</TableCell>
<TableCell component="th" scope="row">
{row.ticker}
</TableCell>
<TableCell align="right">{row.price}€</TableCell>
<TableCell align="right">{row.amount}</TableCell>
<TableCell align="right">{row.date}</TableCell>
<TableCell align="right">{row.currentPrice}€</TableCell>
<TableCell align="right" style={{color: `${row.profitLoss <= 0 ? "red" : "green"}`}}>
{row.profitLoss}€
</TableCell>
<TableCell align="right">{row.comments}</TableCell>
</TableRow>
))) : (
<span style={{color: "red"}}>Couldn't retrieve data</span>
)}
</TableBody>
</Table>
</TableContainer>
<button onClick={() => getOrders()}>Update table</button>
</div>
</div>
) : (
<block>
<h1>You are not logged in!</h1>
<a href="/login">Go back</a>
</block>
)}
</div>
)
}
StockFetcher.js
这里发生了所有外部 api 获取和计算
//Function which fetches the current prices and updates our state with current prices and profit/loss
const stockFetcher = (stocks, setStocks, profitLossCalculator) => {
stocks.forEach(async(stock) => {
console.log(stock)
try {
const ticker = stock.ticker
const response = await axios.get(`${STOCK_API_GLOBAL}&symbol=${ticker}&apikey=${TOKEN}`)
const returnedData = await response.data;
console.log(returnedData)
const yesterday = new Date()
yesterday.setDate(yesterday.getDate())
const time = yesterday.toISOString().slice(0, 17).replace("T", " ").concat("00")
const currentPrice = returnedData['Global Quote']['05. price']
const profitLoss = profitLossCalculator(
stock.price,
currentPrice,
stock.amount
);
const stockWithPrice = {
...stock,
currentPrice: currentPrice,
profitLoss,
};
const indexOfStock = stocks.indexOf(stock);
setStocks((stocks) => [
...stocks.slice(0, indexOfStock),
stockWithPrice,
...stocks.slice(indexOfStock + 1),
]);
} catch (error) {
/*The option how to handle the error is totally up to you.
Ideally, you can send notification to the user */
console.log("error with stockFetcher: " + error);
}
});
return setStocks
};
export default stockFetcher;
我想我的问题是为什么 useState 不在 fetch 中设置自己。如果它在第一次渲染时设置,那么一切都会好起来的。
【问题讨论】:
-
获取远程数据是异步的,不会阻塞渲染,所以如果你调用
setState然后做一些异步的东西(比如用axios获取数据),这将再次调用setState,react 将呈现在对setState的调用之间。如果您只希望您的应用在所有提取完成后重新呈现,您需要先等待所有结果,然后才调用setState获取所有汇总结果。
标签: javascript reactjs api fetch use-effect