如何优化React/Next.js项目中冗余复杂的表单提交处理函数?
React/Next.js表单提交逻辑臃肿优化方案
核心思路
你当前所有提交函数的4步流程是完全固定的,差异仅在每一步的具体业务实现,完全可以用「固定流程骨架 + 场景化回调参数」的模式实现抽离,不需要每个场景写重复的流程代码。
具体优化步骤
1. 封装通用提交流程自定义Hook
先把所有提交函数共有的逻辑抽成通用骨架,不同场景的差异化逻辑通过参数传入:
// 通用提交Hook封装 const useFormSubmit = () => { const dispatch = useDispatch(); const handleSubmit = async (e, callbacks) => { e?.preventDefault?.(); const { validate, processData, apiCall, action, onFinally } = callbacks; try { // 1. 校验:校验不通过直接终止 const validatePass = validate(); if (!validatePass) return; // 2. 加工业务数据 const { payload, actionData } = processData(); // 3. 调用后端接口 await apiCall(payload); // 4. 派发Redux更新 if (action) dispatch(action(actionData)); // 后续收尾逻辑 onFinally?.(); } catch (err) { console.error(err); alert(ALERT_OTHER); // 统一错误提示,后续可替换为全局Toast组件 } } return { handleSubmit }; }
2. 拆解编辑食材提交的差异化逻辑
把原来揉在一起的校验、数据处理、收尾逻辑全部分拆成独立函数,主流程代码会变得非常简洁:
① 抽离表单校验逻辑
const validateEditFood = () => { if ( editFoodName.current.value.trim().length < 1 || editFoodType.current.value === "DEFAULT" || +editFoodQuantity.current.value < 0 || editFoodQuantity.current.value.trim().length < 1 ) { alert(ALERT_FOOD_EMPTY); return false; } if (!editFoodWeight.current.value.match(WEIGHT_REGEX)) { alert(ALERT_WEIGHT_FORMAT); return false; } return true; }
② 抽离业务数据加工逻辑
const buildEditFoodData = () => { const foodObj = { username: foundUser.username, id: props.row.id, foodId: foundUser.foodId, name: editFoodName.current.value, type: editFoodType.current.value, quantity: editFoodQuantity.current.value, weight: editFoodWeight.current.value, expDate: editFoodExpDate.current.value, key: props.row.key, }; const foodCopy = [...foundUser.food]; const foundUserFoodIndex = foodCopy.findIndex(ele => +ele.id === +props.row.id); foodCopy[foundUserFoodIndex] = { name: editFoodName.current.value, type: editFoodType.current.value, quantity: editFoodQuantity.current.value, weight: editFoodWeight.current.value, expDate: editFoodExpDate.current.value, id: props.row.id, key: props.row.key, }; const payload = { username: foundUser.username, recipesId: foundUser.recipesId, foodId: foundUser.foodId, totalWeight: foundUser.totalWeight - getNumberFromStr(props.row.weight) + getNumberFromStr(editFoodWeight.current.value), totalQuantity: foundUser.totalQuantity - props.row.quantity + +editFoodQuantity.current.value, food: foodCopy, recipes: foundUser.recipes, }; return { payload, actionData: foodObj }; }
③ 抽离收尾逻辑
const afterEditSuccess = () => { // 重置表单 editFoodName.current.value = ""; editFoodType.current.value = ""; editFoodQuantity.current.value = ""; editFoodWeight.current.value = ""; editFoodExpDate.current.value = ""; // 关闭弹窗 props.setShowEditFoodModal(false); }
3. 重构后的编辑提交主逻辑
const { handleSubmit } = useFormSubmit(); const submitEditFoodHandler = (e) => handleSubmit(e, { validate: validateEditFood, processData: buildEditFoodData, apiCall: (payload) => setDoc(doc(db, "users", foundUser.id), payload), action: fridgeActions.editFood, onFinally: afterEditSuccess });
额外优化建议
- 建议把当前的非受控表单(ref拿值)改成受控表单,用useState管理表单字段值,后续可以进一步抽离通用表单组件,校验逻辑也可以更方便的复用
- 零散的alert提示可以统一封装成全局提示组件,避免各个业务代码里散落重复的提示逻辑
- 新增/删除场景的提交函数只需要对应写自己的validate、processData等回调,直接复用通用的useFormSubmit Hook即可,不需要再写重复的try catch、流程判断代码
内容的提问来源于stack exchange,提问作者Martino
相关产品推荐
相关产品推荐

