ReactJS路由跳转时如何跨组件传递products数组数据
问题根因
你传值失败核心是三个代码逻辑错误:
- 你在Login组件里写的
<Account props={products} />是无效代码,React Router跳转路由时会渲染路由配置里挂载的Account组件,不会读取你手动写的组件标签,自然拿不到你传的props - React的
setState是异步更新的,你刚调用完setProducts(parseData)马上判断products.password,拿到的还是更新前的初始空数组,校验逻辑本身就会失效 - 用全局
window.isLoggedin存登录态非常不可靠,页面刷新就会丢失,还容易被前端篡改
方案1:React Router 内置路由state传参(最适配当前场景)
React Router v6的navigate方法支持第二个参数传递state,跳转后目标页面用useLocation钩子就能直接取数据,不需要额外安装依赖。
第一步:修改Login组件的提交逻辑
直接用接口返回的原始数据做校验和传值,不要等异步state更新:
const Submit = async (e) => { e.preventDefault(); const { data } = await Axios.get( `http://localhost:8080/api/login/${email}` ); const parseData = JSON.parse(data.response); // 直接用接口返回数据做密码校验 if(parseData.password === password){ // 删除无效的<Account />标签,通过navigate第二个参数传值 navigate('/account', { state: { userInfo: parseData, isLoggedin: true } }); }else{ navigate('/sign-in'); } };
第二步:修改Account组件接收参数
引入useLocation钩子读取路由传参,加兜底逻辑防止用户直接访问账户页报错:
import { useNavigate, useLocation } from 'react-router-dom'; // 新增引入useLocation const Account = () => { const navigate = useNavigate(); const location = useLocation(); // 从路由state中取数据,加空值兜底 const { userInfo, isLoggedin } = location.state || {}; const check =()=>{ if(!isLoggedin){ navigate('/sign-in'); } } useEffect(() => { check(); }, []); return ( <div className="container d-flex justify-content-center"> <div className="card p-3 py-4"> <div className="text-center"> <h3 className="mt-2">Username</h3> <div className="row mt-3 mb-3"> <div className="col-md-4"> <h5>Email</h5> <span className="num">{userInfo?.email || ''}</span> </div> <div className="col-md-4"> <h5>Location</h5> <span className="num">{userInfo?.location || ''}</span> </div> <div className="col-md-4"> <h5>Username</h5> <span className="num">{userInfo?.username || ''}</span> </div> </div> <hr className="line"></hr> <div className="social-buttons mt-5"> <button className="neo-button"><i className="fa fa-facebook fa-1x"></i> </button> <button className="neo-button"><i className="fa fa-linkedin fa-1x"></i></button> <button className="neo-button"><i className="fa fa-google fa-1x"></i> </button> <button className="neo-button"><i className="fa fa-youtube fa-1x"></i> </button> <button className="neo-button"><i className="fa fa-twitter fa-1x"></i> </button> </div> <div className="profile mt-5"> <button className="profile_button px-5" >Log Out</button> </div> </div> </div> </div> ) }
注意:这个方案的缺点是页面刷新后路由state会丢失,如果需要刷新后保留登录状态,用下面的方案2。
方案2:本地存储持久化(适合需要保留登录态的场景)
如果需要用户刷新页面、重新打开标签页都保持登录状态,登录校验通过后把用户信息存在sessionStorage或者localStorage里,所有页面都可以直接读取。
Login组件修改
if(parseData.password === password){ // 存储时转成JSON字符串 sessionStorage.setItem('isLoggedin', '1'); sessionStorage.setItem('userInfo', JSON.stringify(parseData)); navigate('/account'); }
Account组件取值
const check =()=>{ const isLoggedin = sessionStorage.getItem('isLoggedin'); if(isLoggedin !== '1'){ navigate('/sign-in'); } } // 读取时转回对象,加空值兜底 const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
退出登录时记得清除本地存储的对应字段即可。
方案3:全局状态管理(适合多页面共享用户信息的场景)
如果项目中多个页面都需要用到当前登录用户信息,可以用React Context或者Redux做全局状态,登录成功后把用户信息存在全局Store中,所有组件都可以直接读取。
简单的React Context实现示例:
- 先创建用户上下文
import { createContext, useContext, useState } from 'react'; const UserContext = createContext(); export const UserProvider = ({children}) => { const [user, setUser] = useState(null); return <UserContext.Provider value={{user, setUser}}> {children} </UserContext.Provider> } export const useUser = () => useContext(UserContext);
- 在路由外层包裹Provider
root.render( <UserProvider> <App /> {/* 路由配置放在App组件内 */} </UserProvider> )
- 登录成功后存入全局状态
const { setUser } = useUser(); // 校验通过后执行 setUser(parseData); sessionStorage.setItem('isLoggedin', '1'); navigate('/account');
- 任意页面直接读取全局用户信息
const { user } = useUser();
额外安全提醒
不要在前端做密码明文校验,你现在的逻辑是把数据库中存储的用户密码通过接口拉到前端比对,存在严重安全隐患。正确做法是把邮箱和密码都传给后端,由后端完成密码校验,返回登录结果和用户信息,前端不要接触明文密码做比对。
内容的提问来源于stack exchange,提问作者Divay Kumar
相关产品推荐
相关产品推荐

