1. 问题与最终解决办法
问题: 模拟模型任务运行进程的状态变化,包括"Check data format", "Data preprocessing", "Model core computing", "Output result generation in progress","Model execution finished!"五个状态,useState能够成功设置"Check data format"状态,但是ModelExecuteProcess中接收到的初始状态是"Model core computing",永远都比steps[i]状态晚一步,尝试了很多方法未果,最终替换了useReducer后成功实现状态同步更新。
错误结果:
steps[0]: Check data format
status: ['Check data format']
steps[1]: Data preprocessing
status: ['Check data format', 'Data preprocessing']
steps[2]: Model core computing
status: ['Check data format', 'Data preprocessing', 'Model core computing']
steps[3]: Output result generation in progress
status: ['Check data format', 'Data preprocessing', 'Model core computing', 'Output result generation in progress']
status: ['Check data format', 'Data preprocessing', 'Model core computing', 'Output result generation in progress', 'Model execution finished!']
成功结果:
steps[0]: Check data format
status: ['Data preprocessing']
steps[1]: Data preprocessing
status: [ 'Data preprocessing', 'Model core computing']
steps[2]: Model core computing
status: ['Data preprocessing', 'Model core computing', 'Output result generation in progress']
steps[3]: Output result generation in progress
status: ['Check data format', 'Data preprocessing', 'Model core computing', 'Output result generation in progress']
status: ['Check data format', 'Data preprocessing', 'Model core computing', 'Output result generation in progress', 'Model execution finished!']
部分代码:
- Decision.tsx
{/* useState */}
// const handleRun = () => {
// setRunStatus([]);
// setIsRunning(true);
// // Simulate model execution process update...
// const steps = ["Check data format", "Data preprocessing", "Model core computing", "Output result generation in progress"];
// let i = 0;
// const executeStep = () => {
// if (i < steps.length) {
// console.log("i:", i);
// console.log("steps[i]:", steps[i]);
// setRunStatus(prev => {
// if (steps[i]) {
// return [...prev, steps[i]]
// }
// return prev;
// });
// i++;
// setTimeout(executeStep, 1000);
// } else {
// console.log("Process finished logic triggered.");
// setRunStatus(prev => {
// if (prev[prev.length - 1] !== "Model execution finished!") {
// return [...prev, "Model execution finished!"]
// } return prev;
// });
// }
// }
// setTimeout(executeStep, 100);
// }
{/* useReducer */}
type Action = { type: 'ADD_STEP', payload: string } | { type: 'RESET' };
function runStatusReducer(state: String[], action: Action): String[] {
switch (action.type) {
case 'ADD_STEP':
return [...state, action.payload];
case 'RESET':
return [];
default:
return state;
}
}
const handleRun = () => {
setIsRunning(true);
dispatch({ type: 'RESET' });
const steps = ["Check data format", "Data preprocessing", "Model core computing", "Output result generation in progress"];
let i = 0;
const executeStep = () => {
if (i < steps.length) {
console.log("i:", i);
console.log("steps[i]:", steps[i]);
dispatch({ type: 'ADD_STEP', payload: steps[i] });
i++;
setTimeout(executeStep, 1000);
} else {
dispatch({ type: 'ADD_STEP', payload: "Model execution finished!" });
}
}
executeStep();
}
- ModelExecuteProcess.tsx
import React from "react";
import { CheckCircle, Loader } from "lucide-react";
import { motion } from "framer-motion";
interface ModelExecuteProcessProps {
status: String[];
}
const ModelExecuteProcess: React.FC<ModelExecuteProcessProps> = ({ status }) => {
console.log("status: ", status);
// get the last process status => current running progress status
const lastStatus = status[status.length - 1];
// judge whether the current progress is finished
const isProcessFinished = status.length > 0 && lastStatus?.endsWith("finished!");
const currentStepIndex = isProcessFinished ? status.length : Math.max(0, status.length - 1);
const processSteps = ["Check data format", "Data preprocessing", "Model core computing", "Output result generation in progress", "Model execution finished!"];
return (
......
);
};
export default ModelExecuteProcess;
2. useState:简单直接的状态替换
-
结构
const [state, setState] = useState(initialState);
-
工作原理
setState 函数可以直接接收新的状态值,或者一个接收上一个状态并返回新状态的函数。无论采用哪种方式,setState 的核心作用是替换整个状态值。
-
特征
代码量少,对简单状态非常直观;对于只需要获取和设置单个值的非常方便便捷。但是当状态更新逻辑复杂时,会散布在各个处理函数当中,难以维护,此外会涉及批量处理、竞态情况,这也是我一直无法解决的问题。
3. useReducer:可预测的状态替换
-
结构
const [state, dispatch] = useReducer(reducer, initialState, init);
-
工作原理
reducer 函数:它接收当前的 state 和一个 action 对象,然后根据 action 的类型返回一个新的状态。它将状态的计算逻辑与状态的使用分离开来。
const reducer = (state: String[], action: Action): String[] {
switch (action.type) {
case 'ADD_STEP':
return [...state, action.payload];
case 'RESET':
return [];
default:
return state;
}
};
dispatch 函数: 用于触发状态更新。调用dispatch({ type: 'ADD_STEP', payload: steps[i] }),React 就会将这个 action 传递给 reducer 函数进行状态切换,进行后续的计算操作。
-
特征
所有的状态转换逻辑都封装在reducer函数中,组件内部只需要调用dispatch,代码更具有可读性;并且适合处理包含多个值的复杂状态对象;由于reducer是纯函数,增强了代码的可预测性,当遇到异步、按顺序追加的场景时,dispatch比setState更可靠。
useState 和 useReducer 都是React提供的Hook,用于在函数组件中管理状态。它们都能实现更新状态并触发组件重新渲染,但在使用场景、复杂度和可预测性方面有显著区别。
4. 何时选择哪个Hook?
| 场景 | 推荐 Hook | 原因 |
|---|---|---|
| 简单状态 | useState | 状态是独立的原始值或简单对象,更新逻辑只有一两种 |
| 状态关联 | useReducer | 新状态依赖于前一个或多个旧状态,或者状态更新逻辑分散在多个函数中 |
| 异步序列 | useReducer | 需要严格控制异步操作(如setTimeout或API调用)后的状态更新顺序和内容 |
| 大型应用 | useReducer | 状态逻辑需要被共享、复用,或者需要使用Context API来进行全局管理。 |
上述是小李初次接触React的一点点理解,希望能慢慢积累进步,若存在错误请指出,谢谢大家!!!