React+Redux动态路由数据传递异常:Artists组件接收空数组
问题描述
从后端3个不同接口获取数据,目标是在动态路由组件中展示所有数据,但Artists.js始终接收空数组。
已尝试方案:
- 使用
useNavigate传递数据 - 使用
Link传递数据 - 使用Redux Toolkit传递数据(刚接触Redux,代码可能存在问题)
排查发现渲染时机问题:添加条件渲染后,Artists组件第二次渲染时控制台能显示正确数据,但页面一直停留在“loading”状态;尝试在条件渲染后映射数据,刷新页面后渲染内容消失。
补充接口返回结构:
- 接口返回示例:
{artists: {…}, playlists: {…}, tracks: {…}} - artists结构:
artists: {total: 13, artists: Array(13)} - 单个artist对象:
{name: 'Aborted', id: '1XRhUgCyzIdeT8d9KMfeDR', image_url: 'https://i.scdn.co/image/ab6761610000e5eb457a41afcb20ec257fce22d4', popularity: 39, genres: Array(8)}
问题分析与修复方案
核心问题
- Redux数据赋值错误:接口返回的
artists是包含total和artists数组的嵌套对象,但代码直接将整个对象赋值给Redux状态,导致组件中拿到的不是数组,length判断失效。 - Loading状态未更新:
isLoading初始化为true后无逻辑切换为false,组件一直卡在loading分支。 - 路由冲突:
/:genre和/:artist都是动态参数路由,会导致匹配优先级混乱(比如访问艺术家页面时可能匹配到genre路由)。 - 数据加载与路由跳转时机不匹配:点击
Link时先跳转路由再加载数据,组件初始化时Redux还未存入数据。
具体修复步骤
1. 修正Redux数据赋值逻辑
在Genres.js的handleClick中,解构接口返回的嵌套数组:
// Genres.js .then(([artistRes, playlistRes, trackRes]) => { // 提取接口返回中的实际数组 dispatch(setData({ artists: artistRes.artists.artists, playlists: playlistRes.playlists.playlists, tracks: trackRes.tracks.tracks })); })
2. 修复Artists组件的Loading状态
使用useEffect监听Redux中artists的变化,动态更新加载状态:
// Artists.js import React, { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { useSelector } from 'react-redux'; function Artists() { const navigate = useNavigate(); const artists = useSelector((state) => state.spotifyData.artists); const [isLoading, setIsLoading] = useState(true); // 监听artists变化,数据就绪后结束加载 useEffect(() => { if (artists.length > 0) { setIsLoading(false); } }, [artists]); // 加载状态判断:先显示loading,数据为空时显示无数据 if (isLoading) { return <div>Loading...</div>; } if (artists.length === 0) { return <div>No artists found</div>; } // ... 其余点击事件和渲染逻辑不变 }
3. 解决路由冲突
调整路由结构,给动态路由添加明确前缀避免冲突:
// App.js function App() { return ( <Routes> <Route path='/' element={<Genres />} /> {/* 给艺术家路由添加前缀 */} <Route path='/artist/:artist' element={<TopTracks />} /> {/* 给流派路由添加前缀 */} <Route path='/genre/:genre' element={<Results />} /> </Routes> ); }
同步修改跳转路径:
Genres.js中的Link:<Link to={/genre/${genre}} onClick={() => handleClick(genre)}>{genre}</Link>Artists.js中的navigate:navigate(/artist/${artist.name}, { state: { data } });
4. 优化数据加载与跳转时机(可选)
避免先跳转后加载数据,改为加载完成后手动导航:
// Genres.js import { useNavigate } from "react-router-dom"; function Genres() { const navigate = useNavigate(); // ... 其余状态和useEffect不变 function handleClick(genre) { const query_params = { genre: genre }; Promise.all([/* 三个fetch请求 */]) .then((responses) => Promise.all(responses.map(res => res.json()))) .then(([artistRes, playlistRes, trackRes]) => { dispatch(setData({ artists: artistRes.artists.artists, playlists: playlistRes.playlists.playlists, tracks: trackRes.tracks.tracks })); // 数据加载完成后再跳转 navigate(`/genre/${genre}`); }) .catch(error => console.log(error)); } // 将Link改为按钮,避免默认跳转 return ( <div> <div className="genre-list-container"> <ul className="genre-list"> {genres.map((genre) => ( <li className="genre" key={genre}> <button onClick={() => handleClick(genre)}>{genre}</button> </li> ))} </ul> </div> </div> ); }
额外注意事项
- 刷新页面时Redux数据会丢失(内存存储特性),若需支持刷新,可考虑将数据存入
localStorage,或在Results组件中根据路由参数重新请求数据。 - 检查
X-CSRFToken的处理逻辑,确保后端能正常接收请求。
内容的提问来源于stack exchange,提问作者Scottsdaaale
相关产品推荐
相关产品推荐

