React+Redux-Toolkit无法获取currentVideo值问题求助
YouTube克隆项目Redux-Toolkit无法更新currentVideo问题
我正在开发一个YouTube克隆网站,通过Node.js、Axios和Redux-Toolkit从后端获取视频数据,但currentVideo始终为null。不使用dispatch直接将数据存入本地state时能正常获取后端数据,找不到问题所在,请求帮助。
Store.js
import { configureStore, combineReducers } from '@reduxjs/toolkit' import userReducer from '../redux/userSlice.js' import videoReducer from '../redux/videoSlice.js' import storage from 'redux-persist/lib/storage' import { PersistGate } from 'redux-persist/integration/react' import { persistStore, persistReducer, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER, } from 'redux-persist' const persistConfig = { key: 'root', version: 1, storage, } const rootReducer = combineReducers({ user: userReducer, video: videoReducer }) const persistedReducer = persistReducer(persistConfig, rootReducer) export const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }), }) export const persistor = persistStore(store)
videoSlice.js
import { createSlice } from "@reduxjs/toolkit"; const initialState = { currentVideo: null, loading: false, error: false, }; export const videoSlice = createSlice({ name: "video", initialState, reducers: { fetchStart: (state) => { state.loading = true; }, fetchSuccess: (state, action) => { state.loading = false; state.currentVideo = action.payload; }, fetchFailure: (state) => { state.loading = false; state.error = true; }, }, }) export const { fetchStart, fetchSuccess, fetchFailure } = videoSlice.actions export default videoSlice.reducer
Video.jsx
import React, { useEffect, useState } from 'react' import styled from 'styled-components' import ThumbUpAltOutlinedIcon from '@mui/icons-material/ThumbUpAltOutlined'; import ThumbDownAltOutlinedIcon from '@mui/icons-material/ThumbDownAltOutlined'; import ReplayOutlinedIcon from '@mui/icons-material/ReplayOutlined'; import AddTaskOutlinedIcon from '@mui/icons-material/AddTaskOutlined'; import Comments from '../components/Comments' import Card from '../components/Card' import { useDispatch, useSelector } from "react-redux"; import { useLocation } from "react-router-dom"; import axios from 'axios'; import { fetchStart, fetchSuccess, fetchFailure } from '../redux/videoSlice' import { format } from 'timeago.js'; const Container = styled.div` display: flex; gap: 1.5rem; ` const Content = styled.div` flex: 5; ` const Recommendation = styled.div` flex: 2; ` const VideoWrapper = styled.div` ` const Details = styled.div` display: flex; align-items: center; justify-content: space-between; ` const Info = styled.span` color: ${({ theme }) => theme.textSoft}; ` const Hr = styled.hr` border: 0.5px solid ${({ theme }) => theme.soft}; ` const Title = styled.h1` font-size: 18px; font-weight: 400; margin-top: 20px; margin-bottom: 10px; color: ${({ theme }) => theme.text}; ` const Buttons = styled.div` display:flex; gap: 20px; color: ${({ theme }) => theme.text}; ` const Button = styled.div` display: flex; align-items: center; gap:5px; cursor: pointer; ` const Chanel = styled.div` display:flex; justify-content:space-between; ` const ChanelInfo = styled.div` display:flex; gap: 20px; ` const Image = styled.img` width: 50px; height:50px; border-radius: 50%; ` const ChanelDetails = styled.div` display:flex; flex-direction:column; color: ${({ theme }) => theme.text}; ` const ChanelName = styled.span` font-weight: 500;` const ChanelCounter = styled.span` margin-top: 5px; margin-bottom:20px; color: ${({ theme }) => theme.textSoft}; font-size:12px ; ` const Description = styled.p` font-size:12px ; ` const Subscripe = styled.button` background-color: red; font-weight: 500; color: white; border: none; border-radius: 3px; height: max-content; cursor: pointer; padding: 10px 20px; ` const Video = () => { const { currentVideo } = useSelector((state) => state.video); const dispatch = useDispatch(); console.log('currentVideo',currentVideo); const path = useLocation().pathname.split("/")[2]; const [channel, setChannel] = useState({}); // const [video, setVideo] = useState({}); useEffect(()=> { const fetchData = async () => { try { dispatch(fetchStart()) const videoRes = await axios.get(`/videos/find/${path}`); const channelRes = await axios.get(`/users/find/${videoRes.data.userId}`); setChannel(channelRes.data); dispatch(fetchSuccess(videoRes.data)) } catch (err) { dispatch(fetchFailure()) } }; fetchData(); }, [path, dispatch]); return ( <Container> <Content> <VideoWrapper> <iframe width="100%" height="720" src="https://www.youtube.com/embed/vkc99WHcDTk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> </VideoWrapper> <Title>{currentVideo.title}</Title> <Details> <Info>{currentVideo.views} views {format(currentVideo.createdAt)} </Info> <Buttons> <Button> <ThumbUpAltOutlinedIcon /> {currentVideo.likes?.length} </Button> <Button> <ThumbDownAltOutlinedIcon /> Dislike </Button> <Button> <ReplayOutlinedIcon /> Share </Button> <Button> <AddTaskOutlinedIcon /> Save </Button> </Buttons> </Details> <Hr /> <Chanel> <ChanelInfo> <Image src={channel.img} /> <ChanelDetails> <ChanelName>{channel.name}</ChanelName> <ChanelCounter>{channel.subscribers} subscriper</ChanelCounter> <Description>{currentVideo.description}</Description> </ChanelDetails> </ChanelInfo> <Subscripe>Subscripe</Subscripe> </Chanel> <Hr /> <Comments /> </Content> {/* <Recommendation> <Card type="sm"/> <Card type="sm"/> <Card type="sm"/> <Card type="sm"/> <Card type="sm"/> <Card type="sm"/> <Card type="sm"/> <Card type="sm"/> <Card type="sm"/> <Card type="sm"/> <Card type="sm"/> </Recommendation> */} </Container> ) } export default Video
排查方向与解决方案
1. 检查Redux-Persist序列化问题
你的Store配置了redux-persist,有可能后端返回的视频数据包含不可序列化的字段(比如函数、Symbol类型),导致fetchSuccess无法正确更新state。可以先临时移除persist配置测试:
修改Store.js为普通版本:
import { configureStore, combineReducers } from '@reduxjs/toolkit' import userReducer from '../redux/userSlice.js' import videoReducer from '../redux/videoSlice.js' const rootReducer = combineReducers({ user: userReducer, video: videoReducer }) export const store = configureStore({ reducer: rootReducer, })
如果此时currentVideo能正常更新,说明是persist的序列化问题。可以在persistConfig中添加whitelist: ['video']指定只持久化video模块,或者清理后端返回数据中的非序列化字段。
2. 验证Action派发与State更新
打开Redux DevTools,确认:
fetchStart、fetchSuccess是否被正确触发fetchSuccess的payload是否包含完整的视频数据- Redux State中的
video.currentVideo是否有变化
3. 处理组件空值问题
当前组件直接访问currentVideo.title会导致空值报错,影响调试。建议添加可选链操作符:
<Title>{currentVideo?.title}</Title> <Info>{currentVideo?.views} views {currentVideo && format(currentVideo.createdAt)} </Info> <Description>{currentVideo?.description}</Description>
4. 确认路径参数正确性
打印path变量,确认是否正确获取到视频ID,后端接口/videos/find/${path}是否返回预期数据。
内容的提问来源于stack exchange,提问作者Yassin Mo
相关产品推荐
相关产品推荐

