首页 > 解决方案 > 为什么 React 状态在第二次调用时不会改变?

问题描述

我目前正在构建一个利息计算器。的属性值calcInfo可以通过滑块完美地更改,但是一旦我添加calculateResult()任何值,将不再更改并使用默认值。

为什么会发生这种情况,我该如何解决?

const CalculatorContext = React.createContext();
const CalculatorProvider = (props) => {

  const calculatorInformation = {
    initial_investment: 10000,
    monthly_contribution: 1000,
    length_of_time_years: 40,
    interest_rate_per_year: 7,
    financial_data: [],
    result: 2713059,
  }
  const [calcInfo, setCalcInfo] = useState(calculatorInformation);
  
  const changeCalculation = (value, property) => {
      console.log("-- hallo")
      setCalcInfo({...calcInfo, [property]: value})
      setCalcInfo({...calcInfo, financial_data: calculateResult()})
  }

  const calculateResult = () => {
    const initial_investment = calcInfo.initial_investment
    const contribution_per_month = calcInfo.monthly_contribution
    const contribution_per_year = contribution_per_month * 12
    const years = calcInfo.length_of_time_years
    const interest_rate = parseFloat(calcInfo.interest_rate_per_year)
    let result = initial_investment
  
    const years_interests = []
    const years_results = []

    for (let year = 0; year < years; year++) {
      result += contribution_per_year
      result = result / 100 * (100 + interest_rate)
  
      years_interests.push(year)
      years_results.push(result)
    }

    const financial_data = {
      labels: years_interests,
      datasets: [{
          label: 'Vermögensentwicklung über gewählten Zeitraum',
          data: years_results,
      }]
    }

    return financial_data
  }
    
  return (
    <CalculatorContext.Provider value={{calcInfo, changeCalculation}}>
      {props.children}
    </CalculatorContext.Provider>
  )
}
<input className="calculator-slider" type="range" min="1" max="100" step="1" value={length_of_time_years}
        onChange={(e) => context.changeCalculation(parseInt(e.target.value), "length_of_time_years")}></input>

标签: javascriptreactjsuse-state

解决方案


按原样,您的状态设置函数已关闭当前版本的状态(截至该渲染)。确保您始终使用最新版本的状态的一种方法是使用回调函数:

const changeCalculation = (value, property) => {
  setCalcInfo(c => ({...c, [property]: value}))
  setCalcInfo(c => ({...c, financial_data: calculateResult()}))
}

编辑:顺便说一句,如果您需要计算结果calcInfo 内部的更新版本,我建议calcInfo直接传递给calcuateResult或者我猜想将其作为效果运行。

当然,最好的解决方案可能是根本不存储financial_data在状态中,因为它是纯粹的派生状态,因此对于您的其他状态来说是多余的。

关于派生状态

编辑2:只是为了扩展我不存储的意思financial_data。由于它是纯粹的派生状态,因此您可以随时从函数中获取此信息,而不是冗余地存储它。

因此,您可以在某处使此功能独立:

const getFinancialData = (calcInfo) => {
  const initial_investment = calcInfo.initial_investment
  const contribution_per_month = calcInfo.monthly_contribution
  const contribution_per_year = contribution_per_month * 12
  const years = calcInfo.length_of_time_years
  const interest_rate = parseFloat(calcInfo.interest_rate_per_year)
  let result = initial_investment
  
  const years_interests = []
  const years_results = []

  for (let year = 0; year < years; year++) {
    result += contribution_per_year
    result = result / 100 * (100 + interest_rate)

    years_interests.push(year)
    years_results.push(result)
  }

  const financial_data = {
    labels: years_interests,
    datasets: [{
        label: 'Vermögensentwicklung über gewählten Zeitraum',
        data: years_results,
    }]
  }

  return financial_data
}

然后,在您需要此信息的任何组件中,只需调用该函数。届时,您将始终拥有最新信息,而不必担心数据不同步:

const SomeComponent = () => {
  const {calcInfo} = useContect(CalculatorContext);

  // Purely derived state
  const financialData = getFinancialData(calcInfo)
}

推荐阅读