React MUI 组件(滑块/选择)"onChange"不会立即更改值(提供上次单击的结果)



我正在尝试使用Slider/Select来获取用户查询参数,这将对URL进行进一步更改(通过handleChange),并通过fetch钩子进行api调用。

但是,当我将"滑块"值从1更改为0.1时,不会发生任何事情,但如果我将滑块值再次更改为0.1到0.2,它将返回0.1的结果(因此滞后1次单击)。使用"选择"值可以观察到类似的行为。

网址:https://thisorthatstockfrontend.herokuapp.com/rankStockSharpeFiltered

我没有表单控制,但在尝试了表单控制后,似乎并没有解决问题。我真的不想有一个提交按钮,任何帮助都很感激!

import React from 'react'
import { NavLink } from 'react-router-dom'
import { Button,InputLabel  } from '@mui/material'
import { Typography,MenuItem } from '@mui/material'
import { DataGrid } from '@mui/x-data-grid'
import Slider from '@mui/material/Slider';
import { useEffect } from 'react'
import {FormControl} from '@mui/material';
import { useState } from 'react' 
import { useFetch } from '../hooks/useFetch';
import { useForm,Controller } from "react-hook-form"; 
import Select,{SelectChangeEvent}  from '@mui/material/Select';
import Grid  from '@mui/material/Grid'
import { Box } from '@mui/system'; 
export default function StockListedFiltered() 
{

const [values, setValues] = useState(
{
sharpeYear1: 1,
sharpeYear2: 5,
sharperatio1Cutoff: 1,
sharperatio2Cutoff: 1,  
}
);
const [url,seturl]=useState('https://thisorthatstock.herokuapp.com/multipleStockPerformanceFilteredByYear?shortYearNum=' + values.sharpeYear1+'&LongYearNum='+values.sharpeYear2+'&sharpeRatio1='+values.sharperatio1Cutoff+'&sharpeRatio2='+values.sharperatio2Cutoff)

const handleChange = (propertyName) => (event) => {
console.log('hello')
event.preventDefault();
setValues((values) => ({ 
...values,
[propertyName]: event.target.value
}));
console.log('end')
console.log(values)
seturl('https://thisorthatstock.herokuapp.com/multipleStockPerformanceFilteredByYear?shortYearNum=' + values.sharpeYear1+'&LongYearNum='+values.sharpeYear2+'&sharpeRatio1='+values.sharperatio1Cutoff+'&sharpeRatio2='+values.sharperatio2Cutoff)
console.log(url)
};

const{data:stockPerformance ,isPending,error}=useFetch(url) 
const columns=[
{field:'stockName', headerName:'ticker',width:200 },
{field:'years', headerName:'years' ,width:200},
{field:'SharpeRatio', headerName:'sharpe ratio',width:200},
{field:'AnnualReturn', headerName:'annual return',width:200 },
{field:'AnnualVolatility', headerName:'annual volatility',width:200 } 

]; 

return (

<div>
<h2 style={{paddingTop:'3rem',textAlign:'center',paddingBottom:'2rem'}}> Filtered Stocks Ranked  </h2>
<NavLink to="/rankStockSharpe">
<Button>
<Typography>All stock </Typography>
</Button>  
</NavLink> 
<NavLink to="/rankStockSharpeFiltered">
<Button>
<Typography>Filtered </Typography>
</Button>  
</NavLink>  

<Grid container  style={{paddingTop:'2rem',textAlign:'center'}}>
<Grid item xs={2}></Grid>

<Grid item xs={4} style={{display:'flex',alignItems:'center',justifyContent:'center' }}>
<Grid item xs={6}>
<Typography>
Years of Data
</Typography>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<Select
defaultValue={1}
labelId="sharpeYear1"
id="sharpeYear1-select"
value={values.sharpeYear1}
label="Year"
onChange={handleChange("sharpeYear1")}
>
<MenuItem value={1}>1</MenuItem>
<MenuItem value={2}>2</MenuItem>
<MenuItem value={3}>3</MenuItem>
<MenuItem value={4}>4</MenuItem>
<MenuItem value={5}>5</MenuItem> 
</Select>
</FormControl>
</Grid>
<Grid item xs={6}>
<Box sx={{ width: '80%' }}>
<Typography>
Sharpe Ratio cut off 
</Typography>
<Slider 
defaultValue={1}
value={values.sharperatio1Cutoff}
onChange={ handleChange("sharperatio1Cutoff")} 
step={0.05}
valueLabelDisplay="auto" 
min={0}
max={2}
/>
</Box>
</Grid>
</Grid >
<Grid item xs={4} style={{display:'flex',alignItems:'center',justifyContent:'center' }}> 
<Grid item xs={6}>
<Typography>
Years of Data
</Typography>
<Select
defaultValue={5}
labelId="sharpeYear2"
id="sharpeYear2-select"
value={values.sharpeYear2}
label="Year"
onChange={handleChange("sharpeYear2")}
>
<MenuItem value={1}>1</MenuItem>
<MenuItem value={2}>2</MenuItem>
<MenuItem value={3}>3</MenuItem>
<MenuItem value={4}>4</MenuItem>
<MenuItem value={5}>5</MenuItem> 
</Select>
</Grid>
<Grid item xs={6}>
<Box sx={{ width: '80%' }}>
<Typography>
Sharpe Ratio cut off 
</Typography>
<Slider 
defaultValue={1}
value={values.sharperatio2Cutoff}
onChange={handleChange("sharperatio2Cutoff")}  
step={0.05} 
valueLabelDisplay="auto" 
min={0}
max={2}
/> 
</Box>
</Grid>

</Grid>

</Grid>
<div style={{display:'flex',alignItems:'center',justifyContent:'center' }}> 

{isPending&&<div>Loading Data...</div>}
{error &&<div>"This is awkard..."{error}</div>}
{!isPending && stockPerformance&&
<div style={{height:500,width:'70%'}}>
<DataGrid
rows={stockPerformance}
columns={columns}
></DataGrid> 
</div>
} 
</div> 
</div>
)
}

ReactuseState是异步的,因此更新状态&那么在下一行中立即访问它将不起作用。更新状态仅在下一个渲染循环中可用。

以下内容不会对更新后的状态进行控制台日志记录。与setUrl相同。

setValues((values) => ({ 
...values,
[propertyName]: event.target.value
}));
console.log('end')
console.log(values) // state is not yet updated

如果你想对状态更新执行操作,你需要使用useEffect钩子,就像在类组件中使用componentDidUpdate一样,因为useState返回的setter没有回调模式

useEffect(() => {
// action on update of values
console.log(values) // updated state is available here
}, [values]);

您可以克隆values状态,通过更改和更新状态来进行更改。然后使用修改后的对象来计算url,使其具有新的值。

const handleChange = (propertyName) => (event) => {
event.preventDefault();
const newValues = { ...values };
newValues[propertyName] = event.target.value;
setValues(newValues);
seturl(
'https://thisorthatstock.herokuapp.com/multipleStockPerformanceFilteredByYear?shortYearNum=' +
newValues.sharpeYear1 +
'&LongYearNum=' +
newValues.sharpeYear2 +
'&sharpeRatio1=' +
newValues.sharperatio1Cutoff +
'&sharpeRatio2=' +
newValues.sharperatio2Cutoff
);
};

或者更好的是,第二次计算url(不直接在处理程序中),而且它不必处于状态。

const handleChange = (propertyName) => (event) => {
event.preventDefault();
const newValues = { ...values };
newValues[propertyName] = event.target.value;
setValues(newValues);
};
const url = useMemo(() => {
return (
'https://thisorthatstock.herokuapp.com/multipleStockPerformanceFilteredByYear?shortYearNum=' +
values.sharpeYear1 +
'&LongYearNum=' +
values.sharpeYear2 +
'&sharpeRatio1=' +
values.sharperatio1Cutoff +
'&sharpeRatio2=' +
values.sharperatio2Cutoff
);
}, [values]);

顺便说一句,在这个回调中,事件可以是null,对其进行解构,以便将其分配给变量或使用event.persist()

setValues((values) => ({ 
...values,
[propertyName]: event.target.value
}));

最新更新