React组件数据提交异常:数据库存储当前cargo与历史计算值问题求助
React组件数据存储问题解决方案
问题描述
以下是开发的React组件代码:
import './App.css'; import OutputBox from './OutputBox'; import React, { useState } from 'react'; import * as functions from "./functions.js" import axios from 'axios' function App() { //define state variables const [cargo, setCargo] = useState(''); const [acceleration, setAcceleration] = useState(''); const [takeoff_time, setTakeOffTime] = useState(''); const [takeoff_distance, setTakeOffDistance] = useState(''); const [excess_cargo, setExcessCargo] = useState(''); const [date, setDate] = useState(); const [message, setMessage] = useState(''); //send the data to /insert-data in order to store it in the db const insertData = () => { axios.post("http://localhost:3001/insert-data", { takeoff_time: takeoff_time.toString(), takeoff_distance: takeoff_distance.toString(), excess_cargo: excess_cargo.toString(), cargo: cargo.toString() }).then(()=> { console.log("success"); }).catch((error) => { console.error(error); }); console.log(takeoff_time +" | "+ takeoff_distance+" | "+excess_cargo+" | "+cargo); }; //get the cargo data //return if the input is valid const validInput = (cargo) => { if (cargo < 0 || isNaN(cargo)) { alert("invalid input!"); return false; } else { return true; } } //A function that centralizes the actions const handleData = () => { if(validInput(cargo)){ //update the variables every time it change setAcceleration(functions.getAcceleration(cargo)); setTakeOffTime(functions.getTakeOffTime(cargo)); setTakeOffDistance(functions.getTakeOffDistance(cargo)); setExcessCargo(functions.getExcessCargo(cargo)); //call the insert function insertData(); } } //asynchronous func to handle the date data const handleDate = async () => { try { //make a post request to send date data to the back-end const response = await axios.post("http://localhost:3001/date-picker", { date }); setMessage(response.data); //set the changed value to the current variable }catch(error){ //trying to catch errors console.log(error); } } //prints to the web the data return ( <div className='App'> <div className='data'> <h1>Shimshon physics calculator</h1> <label>Enter cargo weight:</label> <input type="number" onChange={ (event) => {setCargo(event.target.valueAsNumber)}}/> <button onClick={handleData}>Send data</button> <OutputBox text={`Acceleration: ${acceleration}`} /> <OutputBox text={`Takeoff Time: ${takeoff_time}`} /> <OutputBox text={`Takeoff Distance: ${takeoff_distance}`} /> <OutputBox text={`Excess Cargo: ${excess_cargo}`} /> <br /> <label>Enter flight date:</label> <input type= "date" onChange={(event) => { setDate(event.target.value) }} /> <button onClick={handleDate}>Send date</button> <OutputBox text={`you ${ message } able to filght`} /> </div> </div> ); } export default App;
运行时遇到的问题:
- 输入货运重量并点击「Send data」按钮,数据库表中仅能接收到
cargo值,takeoff_time、takeoff_distance、excess_cargo字段为空; - 再次输入货运重量并点击按钮,数据库中存储的是当前
cargo值与上一次计算得到的takeoff_time、takeoff_distance、excess_cargo值。
原因分析
React中useState的更新是异步批量处理的,在handleData函数中,调用setTakeOffTime、setTakeOffDistance等更新state后,立刻执行insertData,此时组件的state还未完成更新,insertData读取到的是更新前的旧值(第一次点击时为空状态)。第二次点击时,state已经更新为第一次的计算结果,所以会带上上次的值。
解决方案
方案一:直接使用计算后的值发送请求(推荐)
先计算出所有需要的结果,将其存入变量,同时用这些变量更新state和发送请求,跳过依赖state异步更新的环节:
// 修改handleData函数 const handleData = () => { if(validInput(cargo)){ // 先计算所有结果,存入变量 const newAcceleration = functions.getAcceleration(cargo); const newTakeoffTime = functions.getTakeOffTime(cargo); const newTakeoffDistance = functions.getTakeOffDistance(cargo); const newExcessCargo = functions.getExcessCargo(cargo); // 更新state setAcceleration(newAcceleration); setTakeOffTime(newTakeoffTime); setTakeOffDistance(newTakeoffDistance); setExcessCargo(newExcessCargo); // 直接传入计算后的值调用insertData insertData(newTakeoffTime, newTakeoffDistance, newExcessCargo, cargo); } } // 修改insertData函数,接收参数 const insertData = (takeoff_time, takeoff_distance, excess_cargo, cargo) => { axios.post("http://localhost:3001/insert-data", { takeoff_time: takeoff_time.toString(), takeoff_distance: takeoff_distance.toString(), excess_cargo: excess_cargo.toString(), cargo: cargo.toString() }).then(()=> { console.log("success"); }).catch((error) => { console.error(error); }); console.log(takeoff_time +" | "+ takeoff_distance+" | "+excess_cargo+" | "+cargo); };
方案二:使用useEffect监听state变化发送请求
通过useEffect监听计算后state的变化,当值更新完成后再执行insertData:
// 首先引入useEffect import React, { useState, useEffect } from 'react'; // 在组件内部添加useEffect useEffect(() => { // 仅当所有计算字段和cargo都有有效值时发送请求 if(takeoff_time && takeoff_distance && excess_cargo && cargo){ insertData(); } }, [takeoff_time, takeoff_distance, excess_cargo, cargo]); // 依赖项为需要监听的state // 修改handleData函数,移除内部的insertData调用 const handleData = () => { if(validInput(cargo)){ setAcceleration(functions.getAcceleration(cargo)); setTakeOffTime(functions.getTakeOffTime(cargo)); setTakeOffDistance(functions.getTakeOffDistance(cargo)); setExcessCargo(functions.getExcessCargo(cargo)); } }
注意:方案二需要避免初始渲染时的空值触发请求,因此添加了判断条件;如果用户快速多次点击,可能会触发多次请求,可根据需求添加防抖逻辑。
内容的提问来源于stack exchange,提问作者Tal Roz
相关产品推荐
相关产品推荐

