简体   繁体   English

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

[英]React MUI components (Slider/Select) "onChange" doesn't change value immediately (provide result of previous click)

I'm trying to use Slider/Select to get user query parameters, which will make further changes to the URL (through handleChange) and make api calls through fetch hooks.我正在尝试使用 Slider/Select 获取用户查询参数,这将进一步更改 URL(通过 handleChange)并通过获取挂钩进行 api 调用。

However, when I change the Slider value from 1 to 0.1, nothing happens, but if I change the slider value again to 0.1 to 0.2, it will return me the result of 0.1 ( therefore lagging by 1 click).但是,当我将 Slider 的值从 1 更改为 0.1 时,没有任何反应,但是如果我再次将 slider 的值更改为 0.1 到 0.2,它将返回 0.1 的结果(因此滞后于 1 次点击)。 Similar behavior is observed with the Select value.使用 Select 值观察到类似的行为。

Website here: https://thisorthatstockfrontend.herokuapp.com/rankStockSharpeFiltered网站在这里: https://thisorthatstockfrontend.herokuapp.com/rankStockSharpeFiltered

I don't have form control but after trying form control it didn't seem to fix the issue.我没有表单控件,但在尝试表单控件后似乎没有解决问题。 I really do not want to have a submit button, any help is appreciated!我真的不想有一个提交按钮,任何帮助表示赞赏!

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>
  )
}


React useState is asynchronous , so updating state & then accessing it immediately in next line will not work. React useState异步的,所以更新 state 然后在下一行立即访问它是行不通的。 Updated state is only available on the next render cycle.更新 state 仅在下一个渲染周期可用。

The below will not console log the updated state. Same for setUrl .下面将不会控制台记录更新的setUrl也是如此。

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

If you want to perform an action on state update, you need to use the useEffect hook, much like using componentDidUpdate in class components since the setter returned by useState doesn't have a callback pattern如果要对 state 更新执行操作,则需要使用useEffect挂钩,就像在 class 组件中使用 componentDidUpdate 一样,因为useState返回的 setter 没有回调模式

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

You can clone the values state, make your changes by mutating and update your state. Then use the modified object to calculate the url so it has the new values.您可以克隆values state,通过变异和更新 state 进行更改。然后使用修改后的 object 计算 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
  );
};

Or even better, calculate url in a second time (not directly in your handler), plus it doesn't have to be in the state.或者甚至更好,第二次计算 url(不是直接在您的处理程序中),而且它不必在 state 中。

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]);

By the way, event could be null in this callback, destructure it so it's assigned to a variable or use event.persist() .顺便说一下,在此回调中事件可能是null ,对其进行解构以便将其分配给变量或使用event.persist()

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

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

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