使用useEffect页面加载时调用Spotify Web API获取歌单报400错误
问题:React组件初始化时调用Spotify API获取歌单报400错误,提交操作后调用却正常
控制台错误信息:
GET https://api.spotify.com/v1/me/playlists 400 (Bad Request)
Uncaught (in promise) Error: Failed to fetch playlists custom error
at DashboardPage.tsx:44:17
上述错误重复出现两次。
相关代码:
import { IconPlaylistAdd } from '@tabler/icons-react' import { useEffect, useState } from 'react' import { useDisclosure, useInputState } from '@mantine/hooks'; import { Modal, Button, TextInput, Image } from '@mantine/core'; export default function Dashboard() { const [playlistName, setPlaylistName] = useInputState(''); const [playlistDescription, setPlaylistDescription] = useInputState(''); const [playlists, setPlaylists] = useState([]); const userId = 'hidden for privacy' const [token, setToken] = useState(''); useEffect(() => { const token = localStorage.getItem('token') setToken(`${token}`) fetchPlaylists() console.log(token) }, []) const [opened, { open, close }] = useDisclosure(false); const renderItem = (playlist: any, index: number) => { const imageUrl = playlist.images.length > 0 ? playlist.images[0].url : 'https://placekitten.com/300/300'; return ( <div key={index}> <Image radius='sm' src={imageUrl} w={150} h={150} /> <div>{`${playlist.name}`}</div> </div> ); }; const fetchPlaylists = () => { fetch('https://api.spotify.com/v1/me/playlists', { headers: { 'Authorization': `Bearer ${token}` } }) .then((response) => { if (response.ok) { return response.json(); } else { throw new Error('Failed to fetch playlists custom error'); } }) .then((data) => { setPlaylists(data.items) console.log(data.items) }) } function handleSubmit() { fetch(`https://api.spotify.com/v1/users/${userId}/playlists`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ 'name': `${playlistName}`, 'description': `${playlistDescription}`, 'public': false }) }); setTimeout(fetchPlaylists, 500) close() } return ( <div className="Container"> <Modal opened={opened} onClose={close} title="Create Playlist"> <TextInput value={playlistName} placeholder='Playlist name...' onChange={setPlaylistName} /> <TextInput value={playlistDescription} placeholder='Playlist description...' onChange={setPlaylistDescription} /> <Button type='submit' onClick={handleSubmit}>Submit</Button> </Modal> <IconPlaylistAdd onClick={open} size='150' /> <div>{playlists.map((playlist, index) => renderItem(playlist, index))}</div> </div> ); }
问题原因
核心问题是React的setState是异步操作:
- 初始化的useEffect中,调用
setToken(${token})后立刻执行fetchPlaylists(),此时组件状态里的token还是初始空字符串,导致请求头的Authorization为Bearer,触发Spotify API的400错误。 - 点击提交按钮时,组件已完成渲染,
token状态已更新为localStorage中的有效值,因此调用fetchPlaylists()能拿到正确token,请求成功。
解决方案
方案1:直接使用localStorage获取的token调用接口
不依赖组件状态,直接将localStorage拿到的token传入fetchPlaylists:
useEffect(() => { const storedToken = localStorage.getItem('token') setToken(storedToken || '') // 直接用storedToken调用,无需等待状态更新 fetchPlaylists(storedToken) }, []) // 修改fetchPlaylists,接收token参数 const fetchPlaylists = (authToken?: string) => { const currentToken = authToken || token if (!currentToken) return // 无token时直接返回,避免无效请求 fetch('https://api.spotify.com/v1/me/playlists', { headers: { 'Authorization': `Bearer ${currentToken}` } }) .then((response) => { if (response.ok) { return response.json(); } else { throw new Error('Failed to fetch playlists custom error'); } }) .then((data) => { setPlaylists(data.items) console.log(data.items) }) } // handleSubmit中调用时无需传参,使用状态中的token即可 function handleSubmit() { // ...原有代码 setTimeout(() => fetchPlaylists(), 500) }
方案2:监听token状态变化,更新时调用接口
给useEffect添加token依赖,当token从空字符串更新为有效值时自动调用接口:
// 初始化仅获取token并存入状态 useEffect(() => { const storedToken = localStorage.getItem('token') setToken(storedToken || '') }, []) // 新增useEffect,监听token变化 useEffect(() => { // 仅当token存在时调用 if (token) { fetchPlaylists() } }, [token]) // 依赖token,状态更新时触发
方案3:优化token初始化逻辑,避免异步问题
组件初始化时直接同步获取token并赋值,跳过useState的异步更新:
// 直接将token初始化为localStorage的值,避免异步问题 const [token, setToken] = useState(localStorage.getItem('token') || '') useEffect(() => { fetchPlaylists() }, [])
额外建议
- 在
fetchPlaylists中添加token校验,避免空token发起无效请求 - 处理fetch的网络异常,避免控制台出现未捕获报错
内容的提问来源于stack exchange,提问作者NeemaToto
相关产品推荐
相关产品推荐

