为什么 React 状态在第二次调用时不会改变?

Why does React state not change when called a second time?

我目前正在构建一个利息计算器。 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>

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

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

编辑: 顺便说一下,如果你需要 calcInfo inside of 计算结果的更新版本,我推荐将 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)
}