You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中页面间传递videoUrl跳转后视频无法显示的问题求助

React路由传递视频URL后视频无法播放的解决方法

问题描述

点击表格内的“Watch Video”链接时,将videoUrl传递到VideoFeedback页面,但跳转后视频无法正常显示。直接在浏览器粘贴从数据库获取的该视频URL可以正常播放,使用了React-Router-DOM的Link组件和useParams钩子实现跳转传参。

相关代码

ListVideos页面代码

import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper } from '@mui/material';
import axios from 'axios';

const ListVideos = () => {
  const [videos, setVideos] = useState([]);
  const [distinctAssignments, setDistinctAssignments] = useState([]);
  const [selectedAssignment, setSelectedAssignment] = useState('');
  const [visibleCount, setVisibleCount] = useState(3);
  const [showMore, setShowMore] = useState(false);
  const [error, setError] = useState('');

  // Fetch videos from the API
  useEffect(() => {
    const fetchVideos = async () => {
      try {
        const response = await axios.get('http://localhost:5000/video-submissions');
        setVideos(response.data);

        // Extract distinct assignment names after fetching videos
        const uniqueAssignments = [...new Set(response.data.map(video => video.assign_name))];
        setDistinctAssignments(uniqueAssignments);
      } catch (error) {
        console.error('Error fetching videos:', error);
        setError('Failed to load videos. Please try again later.');
      }
    };

    fetchVideos();
  }, []);

  const toggleShowMore = () => {
    setShowMore(!showMore);
    setVisibleCount(showMore ? 3 : videos.length);
  };

  // Filter videos by selected assignment name
  const filteredVideos = videos.filter(video =>
    selectedAssignment ? video.assign_name === selectedAssignment : true
  );

  return (
    <div className="page">
      <header>
        <div className="container">
          <h1 className="page-heading">List of Video Submissions</h1>
          <ul className="linksList">
            <li>
              <Link to="/" className="link">Landing Page</Link>
            </li>
            <li>
              <Link to="/watch-feedback/:id" className="link">Watch Video</Link>
            </li>
          </ul>
        </div>
      </header>

      <div className="page-Container">
        <div className="assignment-container">
          <label htmlFor="assignment">Filter by Assignment Name:</label>
          <select
            value={selectedAssignment}
            onChange={(e) => setSelectedAssignment(e.target.value)}
            aria-label="Select Assignment"
            style={{ marginBottom: '10px', padding: '8px', width: '100%' }}
          >
            <option value="">Select an assignment...</option>
            {distinctAssignments.length > 0 ? (
              distinctAssignments.map((assignment, index) => (
                <option key={index} value={assignment}>
                  {assignment}
                </option>
              ))
            ) : (
              <option disabled>No assignments available</option>
            )}
          </select>
        </div>

        {error && <div className="error-message">{error}</div>}

        <TableContainer component={Paper} className="fixed-table-container">
          <Table stickyHeader>
            <TableHead>
              <TableRow>
                <TableCell><strong>Assignment Name</strong></TableCell>
                <TableCell><strong>Submitted By (User ID)</strong></TableCell>
                <TableCell><strong>Time Submitted</strong></TableCell>
                <TableCell><strong>Assignment ID</strong></TableCell>
                <TableCell><strong>Video URL</strong></TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {filteredVideos.slice(0, visibleCount).map((video) => (
                <TableRow key={video.videoId}>
                  <TableCell>{video.assign_name}</TableCell>
                  <TableCell>{video.user_id}</TableCell>
                  <TableCell>{new Date(video.upload_date).toLocaleString()}</TableCell>
                  <TableCell>{video.assignment_id}</TableCell>
                  <TableCell>
                    <Link 
                      to={`/watch-feedback/${encodeURIComponent(video.videoUrl)}`} // Use encodeURIComponent for safety
                      onClick={() => console.log(video.videoUrl)} // Log the video URL for debugging
                    >
                      Watch Video
                    </Link>
                  </TableCell>
                </TableRow>
              ))}
            </TableBody>
          </Table>
        </TableContainer>

        <button className="get-started-button" onClick={toggleShowMore}>
          {showMore ? 'Show Less' : 'Show More'}
        </button>
      </div>
    </div>
  );
};

export default ListVideos;

VideoFeedback页面代码

import React, { useState } from 'react';
import { Link, useParams } from 'react-router-dom';
import axios from 'axios';

const VideoFeedback = () => {
  const { videoUrl } = useParams(); // Get videoUrl from URL parameters
  const [mark, setMark] = useState('');
  const [comment, setComment] = useState('');

  const handleSubmitFeedback = async (e) => {
    e.preventDefault();
    
    try {
      const response = await axios.post('/api/marks/submit-feedback', {
        videoUrl, // Use videoUrl directly here
        mark,
        comment,
      });
      alert(response.data.message);
    } catch (error) {
      console.error('Error submitting feedback:', error);
      alert('Failed to submit feedback. Please try again.');
    }
  };

  return (
    <div className="page">
      <header>
        <div className="container">
          <h1 className="page-heading">Watch Video and Provide Feedback</h1>
          <ul className="linksList">
            <li><Link to="/" className="link">Landing Page</Link></li>
            <li><Link to="/list-videos" className="link">Back to Video List</Link></li>
          </ul>
        </div>
      </header>

      <div className="page-Container">
        {/* Video Player */}
        {videoUrl ? (
          <video width="640" height="360" controls autoPlay>
            <source src={decodeURIComponent(videoUrl)} type="video/mp4" />
            Your browser does not support the video tag.
          </video>
        ) : (
          <p>No video available to play.</p>
        )}

        {/* Feedback Form */}
        <form onSubmit={handleSubmitFeedback} className="feedback-form">
          <div className="form-group">
            <label htmlFor="mark">Mark:</label>
            <input
              type="number"
              id="mark"
              value={mark}
              onChange={(e) => setMark(e.target.value)}
              required
              min="0"
              max="100"
              placeholder="Enter mark out of 100"
            />
          </div>
          <div className="form-group">
            <label htmlFor="comment">Commentary Feedback:</label>
            <textarea
              id="comment"
              value={comment}
              onChange={(e) => setComment(e.target.value)}
              required
              placeholder="Enter your feedback here"
            ></textarea>
          </div>
          <button type="submit" className="feedback-Sub">Submit Feedback</button>
        </form>
      </div>
    </div>
  );
};

export default VideoFeedback;

App.js代码

import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import LandingPage from './pages/LandingPage';
import LoginPage from './pages/LoginPage';
import UserAdminPage from './pages/UserAdminPage';
import ListAssignments from './pages/ListAssignments';
import CreateAssignment from './pages/CreateAssignment';
import ListVideos from './pages/ListVideos';
import VideoFeedback from './pages/VideoFeedback';
import StudentPage from './pages/studentPage';
import LecturerPage from './pages/lecturerPage';

import logo from './logo.svg';
import './App.css';

const App = () => {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<LandingPage />} />
        <Route path="/login" element={<LoginPage />} />
        <Route path="/user-admin" element={<UserAdminPage />} />
        <Route path="/list-assignments" element={<ListAssignments />} />
        <Route path="/create-assignment" element={<CreateAssignment />} />
        <Route path="/list-videos" element={<ListVideos />} />
        <Route path="/watch-feedback/:id" element={<VideoFeedback />} />
        <Route path="/user-student" element={<StudentPage />} />
        <Route path="/user-lecturer" element={<LecturerPage />} />
      </Routes>
    </Router>
  );
};

export default App;

问题排查与修复

1. 路由参数名称不匹配(核心问题)

在App.js中,路由定义为/watch-feedback/:id,但在VideoFeedback组件中,你尝试获取的参数是videoUrl:

const { videoUrl } = useParams();

参数名称必须完全一致,有两种修改方式:

  • 方式一:修改路由定义
    将App.js中的路由改为:

    <Route path="/watch-feedback/:videoUrl" element={<VideoFeedback />} />
    

    这样组件中useParams()获取的videoUrl就能匹配到路由参数。

  • 方式二:修改组件内参数名
    在VideoFeedback组件中改为获取id参数,再解码使用:

    const { id } = useParams();
    // 后续使用时解码:decodeURIComponent(id)
    

2. 验证URL编码解码的正确性

虽然你已经使用encodeURIComponent()处理了传递的URL,但建议在VideoFeedback组件中打印解码后的URL,确认是否与原URL一致:

const { videoUrl } = useParams();
const decodedUrl = decodeURIComponent(videoUrl);
console.log('Decoded Video URL:', decodedUrl); // 检查控制台输出是否正确

确保解码后的URL和直接粘贴到浏览器的URL完全相同,没有字符丢失或转义错误。

3. 视频标签的额外检查

  • 确认video标签的type属性与视频格式匹配,如果视频不是mp4,需要修改type值(比如video/webm)。
  • 检查是否存在跨域问题:如果视频存储在外部服务器,需要确保服务器配置了CORS允许你的前端域名访问。

修改后的关键代码示例

修改后的App.js路由

<Route path="/watch-feedback/:videoUrl" element={<VideoFeedback />} />

修改后的VideoFeedback组件(验证解码)

const VideoFeedback = () => {
  const { videoUrl } = useParams();
  const decodedUrl = decodeURIComponent(videoUrl);
  console.log('Decoded URL:', decodedUrl);
  
  const [mark, setMark] = useState('');
  const [comment, setComment] = useState('');

  const handleSubmitFeedback = async (e) => {
    e.preventDefault();
    
    try {
      const response = await axios.post('/api/marks/submit-feedback', {
        videoUrl: decodedUrl,
        mark,
        comment,
      });
      alert(response.data.message);
    } catch (error) {
      console.error('Error submitting feedback:', error);
      alert('Failed to submit feedback. Please try again.');
    }
  };
  
  return (
    <div className="page">
      {/* 其余代码省略 */}
      <div className="page-Container">
        {decodedUrl ? (
          <video width="640" height="360" controls autoPlay>
            <source src={decodedUrl} type="video/mp4" />
            Your browser does not support the video tag.
          </video>
        ) : (
          <p>No video available to play.</p>
        )}
        {/* 表单代码省略 */}
      </div>
    </div>
  )
}

内容的提问来源于stack exchange,提问作者Charmaine Msibi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 06:27:02