如何持久化useLocation()路由传参数据及解决localStorage解析问题
我目前需要传递Firebase中嵌套集合里的数据,以下背景说明可直接跳转到核心问题部分。
通常如果只有单个集合,不会遇到相关问题,你可以在App.js中将用户UID传递到各个页面,示例如下:
function App() { const [user, setUser] = useState([]); useEffect(() => { auth.onAuthStateChanged((authUser) => { if (authUser) { setUser(authUser); } else { setUser(false); } }) }, []) return ( <div> <Router> <Switch> <Route path = "/SomePath"> <SomeLocation user={user}/> </Route> </Switch> </Router> </div> ); } export default App;
由于用户对象包含你需要的所有基础数据,通常不需要担心嵌套集合的访问问题,但如果使用多层嵌套集合会有什么问题?
如果要访问嵌套集合的全部数据也没问题,只需要用户信息即可,示例如下:
useEffect(() => { const dbRef= db.collection("users").doc(user.uid).collection("someCollection") dbRef.onSnapshot(snapshot => { const tempData = []; snapshot.forEach((doc) => { const data = doc.data(); tempData.push(data); }); setDataSomewhere(tempData); }) }, [user]);
但据我所知,仅通过用户信息无法获取嵌套集合中特定文档的UID数据。
核心问题部分
举个实际示例:我的业务场景是家长账号下添加学生数据,Firebase的集合结构为users -> [家长UID] -> students -> [学生UID] -> 学生详情字段,编辑表单的路由地址为独立路径/EDIT_STUDENT。
我现在的实现逻辑是:在数据表格页勾选学生后点击编辑图标,通过useLocation()传递学生数据到编辑页,代码如下:
//PATH-A (数据表格所在页面) const editStudent= { pathname: '/EDIT_STUDENT', data: studentData } const editStudents= () => { if(studentData== 0) { window.alert("Select a student") } else { history.push(editStudent); } }; ///EDIT_STUDENT (编辑表单页) let studentData= useLocation(); let data = studentData.data; console.log(data) const uid = data[0].uid // 这里可以拿到选中学生的专属UID
控制台打印的返回数据正常,但刷新页面后所有数据都会消失,因为无法再从前一个路由获取useLocation传递的数据,我的问题是:刷新页面时如何保留useLocation()传递过来的数据?
欢迎提供任何帮助、建议或代码示例。
补充说明
刷新页面后会报属性读取失败的错误。
更新
我参考建议尝试用localStorage存储数据,但读取时返回的是字符串,无法拆分访问对应字段,请问该如何解决?我存储数据的代码如下:
useEffect(() => { const localStorageEstData = window.localStorage.getItem("students"); localStorageEstData && setStudentsData(JSON.parse(localStorageEstData)); }, []); useEffect(() => { window.localStorage.setItem("students", JSON.stringify(studentsData)); }, [studentsData]);
我读取数据的代码及控制台输出如下:let data = window.localStorage.getItem("estudiantes")
数据以完整字符串形式返回,我该如何解析拆分?
解决方案
最优方案:将学生ID放到路由参数中(推荐)
这是最符合前端路由设计规范的方案,不存在本地数据和服务端不一致的问题,刷新后也能正常读取数据。
- 修改路由定义,新增学生ID参数:
// App.js中的路由配置 <Route path="/EDIT_STUDENT/:studentId"> <EditStudent user={user}/> </Route>
- 修改表格页跳转逻辑:
const editStudents= () => { if(!studentData || studentData.length === 0) { window.alert("请先选择学生") } else { // 取选中的第一个学生的UID作为路由参数 history.push(`/EDIT_STUDENT/${studentData[0].uid}`); } };
- 编辑页读取参数并拉取最新数据:
import { useParams } from 'react-router-dom'; function EditStudent({user}) { // 读取路由中的学生ID参数 const { studentId } = useParams(); const [studentData, setStudentData] = useState(null); // 用ID直接从Firebase拉取最新的学生数据 useEffect(() => { if(!user?.uid || !studentId) return; const studentRef = db.collection("users").doc(user.uid).collection("students").doc(studentId); const unsubscribe = studentRef.onSnapshot(snapshot => { if(snapshot.exists) { setStudentData(snapshot.data()); } }) // 组件卸载时取消监听 return () => unsubscribe(); }, [user, studentId]); // 后续直接用studentData渲染表单即可 }
方案二:修复localStorage使用问题
你当前使用localStorage的问题有两个:
- 存储和读取的key不一致:存储时用的key是
students,读取时用的是estudiantes,无法命中存储内容 - 读取后没有反序列化:localStorage只能存储字符串,读取后需要用
JSON.parse转成JS对象才能访问字段
修正后的编辑页读取逻辑:
import { useLocation } from 'react-router-dom'; function EditStudent() { const [studentData, setStudentData] = useState(null); const location = useLocation(); useEffect(() => { let rawData = location.data; // 如果路由中没有拿到数据,就从localStorage取 if(!rawData) { const localString = window.localStorage.getItem("students"); if(localString) { // 反序列化为JS对象 rawData = JSON.parse(localString); } } if(rawData) { setStudentData(rawData); } }, [location]) }
注意该方案的局限性:用户清除浏览器缓存后数据会丢失,同时打开多个编辑页时存储内容会互相覆盖,仅适合临时使用。
内容的提问来源于stack exchange,提问作者React Enjoyer

