React表单双向绑定:编辑按钮传值预填充表单遇无限循环问题
解决React食谱项目编辑功能的无限循环问题
核心问题分析
你之前尝试实现编辑功能时出现无限循环,主要原因有两个:
- 传递给子组件的回调函数未用
useCallback包裹,导致父组件每次渲染都生成新函数,触发子组件重复渲染 - 原有
useEffect依赖项包含isLoading/isDeleting,状态更新会反复触发数据请求,形成循环
分步修改实现编辑功能并解决循环问题
1. 改造App.js,添加编辑状态与回调
在App组件中新增存储编辑食谱的状态,同时优化原有useEffect的依赖项:
function App() { // 新增:存储待编辑的食谱数据 const [editingRecipe, setEditingRecipe] = useState(null); const [user, setUser] = useState({}); const [recipes, setRecipes] = useState([]); const [isLoading, setIsLoading] = useState(false); const [isDeleting, setIsDeleting] = useState(false); // ... 原有登录/登出逻辑保持不变 // 新增:接收RecipeTable传来的编辑数据 const startEditHandler = useCallback((recipe) => { setEditingRecipe(recipe); }, []); // 修复:移除isLoading/isDeleting依赖,避免无限触发请求 useEffect(() => { fetchRecipesHandler(); setIsLoading(false); setIsDeleting(false); }, [fetchRecipesHandler]); // 新增:编辑完成后更新列表并清空编辑状态 const finishEditHandler = useCallback(() => { setEditingRecipe(null); setIsLoading(true); }, []); return ( <div className="App"> {/* ... 原有登录模块保持不变 */} {user && <> {/* 传递编辑数据和完成回调给RecipeForm */} <RecipeForm onAddRecipe={addRecipeHandler} editingRecipe={editingRecipe} onFinishEdit={finishEditHandler} /> {/* 传递编辑回调给RecipeTable */} <RecipeTable recipeData={recipes} onIsDelete={() => setIsDeleting(true)} onStartEdit={startEditHandler} /> </>} </div> ); }
2. 给RecipeTable的编辑图标绑定点击事件
修改RecipeTable组件,给PencilFill添加点击事件,传递当前行的食谱数据:
function RecipeTable(props) { // ... 原有状态和删除逻辑保持不变 return ( <Container> {/* ... 原有表格头部保持不变 */} <tbody> {props.recipeData.map((item) => ( <tr key={item.id}> {/* ... 原有表格内容保持不变 */} <td> {/* 绑定编辑点击事件,传递当前食谱 */} <PencilFill className="m-2" color="black" onClick={() => props.onStartEdit(item)} ></PencilFill> <Trash className="m-2" onClick={() => removeRecipeHandler(item.id)} color="red" /> </td> </tr> ))} </tbody> </Container> ) }
3. 修改RecipeForm,支持预填充编辑数据
添加useEffect监听编辑数据变化,自动填充表单,同时区分新增/编辑提交逻辑:
function RecipeForm(props) { const [enteredRecipe, setEnteredRecipe] = useState(''); const [enteredIngredients, setEnteredIngredients] = useState(''); const [enteredNotes, setEnteredNotes] = useState(''); const [enteredOnlineLink, setOnlineEnteredLink] = useState(''); const [ingredientList, setIngredientList] = useState([]); // 监听编辑数据变化,自动填充表单 useEffect(() => { if (props.editingRecipe) { const { recipeName, ingredients, notes, onlineLink } = props.editingRecipe; setEnteredRecipe(recipeName); setIngredientList(ingredients || []); setEnteredNotes(notes); setOnlineEnteredLink(onlineLink); } else { // 无编辑数据时清空表单 setEnteredRecipe(''); setIngredientList([]); setEnteredNotes(''); setOnlineEnteredLink(''); } }, [props.editingRecipe]); // ... 原有输入变化、添加/删除配料逻辑保持不变 // 修改提交逻辑,区分新增和编辑 const submitHandler = (event) => { event.preventDefault(); const recipeData = { recipeName: enteredRecipe, ingredients: ingredientList, notes: enteredNotes, onlineLink: enteredOnlineLink }; if (props.editingRecipe) { // 编辑逻辑:发送PUT请求更新数据 fetch(`https://react-demo-65577-default-rtdb.firebaseio.com/recipes/${props.editingRecipe.id}.json`, { method: 'PUT', body: JSON.stringify(recipeData), headers: { 'Content-Type': 'application/json' } }).then(() => { props.onFinishEdit(); }).catch(err => console.log(err)); } else { // 原有新增逻辑 props.onAddRecipe(recipeData); } // 清空输入框 setEnteredRecipe(''); setEnteredIngredients(''); setEnteredNotes(''); setOnlineEnteredLink(''); }; // ... 原有表单渲染保持不变 }
4. 修复删除逻辑的潜在问题
优化removeRecipeHandler,避免依赖内部状态触发不必要的重渲染:
const removeRecipeHandler = useCallback(async (recipeId) => { try { const response = await fetch(`https://react-demo-65577-default-rtdb.firebaseio.com/recipes/${recipeId}.json`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' } }); if (!response.ok) { throw new Error('Something went wrong'); } // 直接通知父组件更新,无需依赖内部isDeleted状态 props.onIsDelete(true); } catch (error) { console.log(error); } }, [props]);
关键优化点
- 所有传递给子组件的回调用
useCallback包裹,避免不必要的重渲染 - 精简
useEffect依赖项,只保留触发数据请求的必要条件 - 用
useEffect监听编辑状态变化,确保表单预填充逻辑稳定
内容的提问来源于stack exchange,提问作者Ryan_Milkiewicz
相关产品推荐
相关产品推荐

