React笔记应用folder_files状态无法保留更新值的问题
笔记应用文件夹复选框状态无法持久化问题
开发笔记应用的文件夹分类功能时,遇到以下问题:调用handleFileAddition函数的add分支更新复选框状态后,folder_files状态会短暂更新,但再次调用该函数时,之前的状态更新会被覆盖,导致复选框无法维持勾选状态。
复选框渲染代码
{folder_files.map(note => { return ( <> <label htmlFor={`note-${note.id}`} className='note' style={{ display: 'block', position: 'relative' }} > {note.para} <input type="checkbox" checked={note.checked} name={`note-${note.id}`} id={`note-${note.id}`} onChange={(e)=> handleFileAddition({ type: "add", val: { id: note.id, check: e.target.checked } })} style={{ position: 'absolute', right: 0, backgroundColor: "inherit" }} /> </label> </> ) })}
核心问题:folder_files状态无法保留更新后的复选框状态,每次操作后会被重置为初始状态。
完整功能实现代码
function useFunctionality() { const initialArr = []; // INITIAL ARRAY BEING ASSIGNED TO REDUCER HOOK // CHOOSING DISPLAY BETWEEN FILES & FOLDER const [choices, setChoices] = useState({ files: true, folder_files: false, choice: false, change: { val: false, id: '', para: '', files: [] } }); // FOR HOLDING THE FOLDERS & THE FILE SUPPOSED TO BE STORED IN THEM const [folders, dispatchFolders] = useReducer(reducerFolder, initialArr); // STATE FOR HOVERING, RIGHT CONTAINER'S CHILDREN DISPLAY & // NEW NOTE ADDITION FUNCTIONALITY const [isChanging, setIsChanging] = useState({ hover: false, r_empty: false, add: false, change: { val: false, inp: '', txt:'', id: null } }); // REDUCER HOOK FOR ADDITION, CHANGE & DELETION OF NOTES const [noteList, dispatch] = useReducer(reducer, initialArr); // FILES WITH THE CHECKED PROPERTY ADDED const [folder_files, setFolderFiles] = useState([]) const inputRef = useRef(''); // REF FOR INPUT FIELD(FOR FILES & FOLDERS) const textRef = useRef(''); // REF FOR TEXT AREA(FOR FILES ONLY) let files; // let vg; // REPRESENTING CHANGE HERE UPDATED STATE IS GETTING SHOWN useEffect(() => { console.log( folder_files, choices.change, folder_files.filter(file => file.checked) ) }, [folder_files]) function idGenerator(notes) { // NOTE'S ID GENERATOR const arr = [...notes]; const result = arr.length !== 0 ? arr[arr.length -1].id + 1 : 1 return result; } function paraGenerator(inp, txt) { // GENERATING NOTES ENTRY FOR DISPLAY let para; switch(true) { case (inp && txt !== '' ): para = inp.slice(0, 15); break; case ((inp || txt) !== '' ): para = inp !== '' ? inp.slice(0, 15) : txt.slice(0, 15); break; default: para = null; break; } console.log(para, inp, txt) return para; } // DISPLAYING FOLDER NAMES OR THE EDITABLE SIDE function handleFolderDisplay( save = false, changer = false, value ) { if (changer) { setIsChanging({ ...isChanging, r_empty: true }); setChoices({ ...choices, change: { val: true, id: value.id, para: value.para, files: [value.folder_files] } }) } else { if (!save) { const idFold = idGenerator(folders); setIsChanging({ ...isChanging, r_empty: true }); setChoices({ ...choices, change: { val: false, id: idFold, para: '', files: [] } }) } else { setIsChanging({ ...isChanging, r_empty: false }); const para = paraGenerator(inputRef.current.value, ''); if (choices.change.val) { setChoices({ ...choices, change: { val: false, id: '', para: '' } }); dispatchFolders({ type: "change", values: { id: choices.change.id, para: inputRef.current.value } }) } else { dispatchFolders({ type: "add", values: { id: "fold" + choices.change.id, para: para, files: files } }); } } } } // THE PROBLEM CODE BLOCK function handleFileAddition(obj){ let ff; switch (obj.type) { case "display": setChoices({ ...choices, folder_files: obj.val }) break; case "add": console.log(folder_files, obj.val.check) setFolderFiles(prevFolderFiles => prevFolderFiles.map(file => { if (file.id === obj.val.id) { console.log( "got it", folder_files, file.checked, obj.val.check ); try { return { ...file, checked: obj.val.check }; } catch { console.log("Error") } } else { console.log("gt", file) return file; } })); break; case "save" : setChoices({ ...choices, folder_files: obj.val }) if (choices.change.val) { dispatchFolders({ type: "file_change", id: choices.change.id, files: folder_files.filter(file=> file.checked) }); setFolderFiles(folder_files.map(file => { return { ...file, checked: false } })) } } } function handleFolderDeletion(taskId) { dispatchFolders({ type: "delete", id: taskId }); } function handleChoices(choiceVal = true, files = choices.files) { choices.files === files ? null : setIsChanging({ ...isChanging, r_empty: false }); setChoices(prevChoices => ({ ...prevChoices, files: files, // Directly set based on files argument choice: choiceVal // Update choice accordingly ? !prevChoices.choice : false, })); const arr = noteList.map(note => note.checked = false) setFolderFiles(noteList.map(note => { return { ...note, checked: false } })) } return { isChanging, choices, inputRef, textRef, noteList, folders, folder_files, handleChanging, dispatch, handleEmptiness, handleDeletion, handleChoices, handleFolderDisplay, handleFolderDeletion, handleFileAddition } }
问题原因分析
- 直接修改状态对象:
handleChoices中noteList.map(note => note.checked = false)直接修改了noteList的原始元素,违反React状态不可变原则,导致状态更新异常。 - Stale State引用:
handleFileAddition的save分支直接使用当前folder_files状态更新,未通过函数式获取最新状态,会覆盖之前的修改。 - 状态重置逻辑错误:
handleChoices重置folder_files时,使用noteList而非folder_files的最新状态,导致复选框状态被强制清空。
修复方案
1. 修复handleChoices函数
改用不可变方式操作状态,基于folder_files最新状态重置复选框:
function handleChoices(choiceVal = true, files = choices.files) { choices.files === files ? null : setIsChanging(prev => ({ ...prev, r_empty: false })); setChoices(prevChoices => ({ ...prevChoices, files: files, choice: choiceVal ? !prevChoices.choice : false, })); // 基于最新的folder_files状态重置复选框 setFolderFiles(prev => prev.map(file => ({ ...file, checked: false }))); }
2. 修复handleFileAddition的save分支
使用函数式更新获取最新状态,避免Stale State:
case "save" : setChoices(prev => ({ ...prev, folder_files: obj.val })); if (choices.change.val) { setFolderFiles(prev => { dispatchFolders({ type: "file_change", id: choices.change.id, files: prev.filter(file=> file.checked) }); return prev.map(file => ({ ...file, checked: false })); }); }
3. 优化handleFileAddition的add分支
移除冗余代码,保持逻辑简洁:
case "add": setFolderFiles(prevFolderFiles => prevFolderFiles.map(file => file.id === obj.val.id ? { ...file, checked: obj.val.check } : file )); break;
验证修复
修改后,所有状态更新均基于最新状态进行,重置操作不会覆盖之前的复选框修改,复选框状态将能够正常持久化显示。
内容的提问来源于stack exchange,提问作者Aman Kaushik
相关产品推荐
相关产品推荐

