react-router-dom <链接>不更新页面 [英] react-router-dom &lt;Link&gt; Not Updating Page

查看:33
本文介绍了react-router-dom <链接>不更新页面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题描述:

通过链接标签更改此 url 的 id(仅数字)不会更新页面(但会更改地址栏中的 url).之后点击刷新将显示更新的页面.

http://localhost:8080/video/id/7564

右键单击以在新选项卡中打开链接,或将链接路径更改为完全不同的页面,按预期工作.

我的 app.js 文件

从 'react' 导入 React从'react-router-dom'导入{路由器,路由,交换机}从'../components/homePage/RenderHomepage'导入RenderHomepage从 '../components/channelPage/RenderChannelPage' 导入 RenderChannelPage从 '../components/videoPage/RenderVideoPage' 导入 RenderVideoPage从 '../components/searchPage/RenderSearchPage' 导入 RenderSearchPage从'../components/PageNotFound'导入PageNotFound从'../history'导入历史const App = () =>{返回 (<div><路由器历史={历史}><开关><Route path="/" 精确组件={RenderHomepage}/><Route path="/channel" component={RenderChannelPage}/><Route path="/video/id" component={RenderVideoPage}/><Route path="/search" component={RenderSearchPage}/><Route path="/404" 精确组件={PageNotFound}/><路由组件={PageNotFound}/></开关></路由器>

)}导出默认应用

UpNextVideos 组件中的链接标签:

从 'react' 导入 React从'react-router-dom'导入{链接}...<Link to={{pathname: vid.id}}><h3 className={`${p}-sidebar-grid-video-title`}>{capitalizeFirstLetter(vid.tags)}</h3></链接>...

有问题的组件是如何嵌套的:

<视频页面><UpNextVideos>

RenderVideoPage 组件:

从 'react' 导入 React从 './VideoPage' 导入 VideoPage从'../Header'导入标题从 '../HeaderMobile' 导入 HeaderMobile从 '../FooterMobile' 导入 FooterMobile从 '../ActivityFeed' 导入 ActivityFeedconst RenderVideoPage = () =>{返回 (<div className="videoPage-body"><HeaderMobile/><标题/><ActivityFeed page={'home'}/><视频页面/><FooterMobile page={'video'}/>

)}导出默认RenderVideoPage

VideoPage 组件:

import React, { useEffect, useState } from 'react'从 'axios' 导入 axios从../../history"导入历史从 './containers/handleMediaQueries' 导入 handleMediaQueries从 './containers/setDislikes' 导入 setDislikes从 './NewSubscribers' 导入 NewSubscribers从 './CommentSection' 导入 CommentSection从 './UpNextVideos' 导入 UpNextVideos从'./DescriptionBox'导入DescriptionBox从 './VideoNotFound' 导入 VideoNotFoundimport { fetchVideoFromID, fetchPictureFromID } from '../../containers/api'从'../svgs'导入{拇指向上,拇指向下}进口 {缩写编号,大写第一个字母,randomDate } 来自'../../containers/helperFunctions'const VideoPage = () =>{const [p, setPrefix] = useState("videoPage")const [状态,设置状态] = useState({加载:真实,错误:假})useEffect(() => {如果(state.loading)extractDataFromUrl()否则 handleMediaQueries()}, [state.loading])const fetchVideo = async (id, picAuthorID) =>{让响应 = 等待 fetchVideoFromID(id)if (!response) setState(prevState => ({...prevState, error: true}))否则 mapVideoResponseToHTML(response.data.hits, picAuthorID)}const mapVideoResponseToHTML = (response, picAuthorID) =>{让 responseAsHtml = response.map(vid => {返回 {视频:<div className={`${p}-video-wrapper posRelative`} key={vid.id}><a className={`${p}-pixabay-src`} href={vid.pageURL}>?</a><视频海报="https://i.imgur.com/Us5ckqm.jpg"className={`${p}-视频可点击`}src={vid.videos.large.url}控制自动播放></视频><div className={`${p}-video-info-wrapper`}><div className={`${p}-video-title-box`}><h1 className={`${p}-video-title`}>{capitalizeFirstLetter(vid.tags)}</h1><span className={`${p}-video-views`}>{abbreviateNumber(Number(vid.downloads).toLocaleString())} 次观看</span><span className={`${p}-video-date`}>{randomDate()}</span>

<div className={`${p}-video-options`}><div className="thumbs"><div className={`${p}-video-options-thumbsUp`}>{thumbsUp(20)} &nbsp;<span className={`${p}-video-options-thumbsUp-text`}>{abbreviateNumber(vid.likes)}</span>

<div className={`${p}-video-options-thumbsDown`}>{thumbsDown(20)} &nbsp;<span className={`${p}-video-options-thumbsDown-text`}>{setDislikes(vid.likes)}</span>

<div className={`${p}-video-options-likebar`}></div>

<span className={`${p}-video-options-share`}>分享</span><span className={`${p}-video-options-save`}>保存</span><span className={`${p}-video-options-ellipses`}>...</span>

,作者追随者:vid.views,vidAuthorID: vid.id,作者: picAuthorID ?'加载':vid.user,作者头像: picAuthorID ?null : vid.userImageURL,意见:vid.downloads}})responseAsHtml = responseAsHtml[0]setState(prevState => ({...prevState, ...responseAsHtml, loading: false}))如果 (picAuthorID) fetchAuthorAvatar(picAuthorID)}const extractDataFromUrl = () =>{const currentURL = window.location.hrefconst urlAsArray = currentURL.split('/')const urlID = urlAsArray[5].split('-')const videoID = urlID[0]const picAuthorID = urlID[1]//除了首页之外,作者头像是随机的.//如果 url 不是来自主页,则使用 videoID//如果 url 来自主页,则发送该头像 ID如果(urlID.includes('000')){fetchVideo(videoID)} 别的 {setState(prevState => ({...prevState, picAuthorID: picAuthorID}))fetchVideo(videoID, picAuthorID)}}const fetchAuthorAvatar = async (id) =>{const 响应 = 等待 fetchPictureFromID(id)const authorName = response.data.hits[0].userconst authorAvatar = response.data.hits[0].previewURLsetState(prevState => ({...prevState,作者头像:作者头像,作者:capitalizeFirstLetter(authorName)}))}返回 (<div>{ 状态错误?<VideoNotFound/>: 空值}{ state.loading === 真?空值:<div className={`${p}-page-wrapper`}><main className={`${p}-main`}>{state.video}<DescriptionBox props={state}/><div className={`${p}-suggested-videos-mobile`}></div><div className={`${p}-new-subscribers-wrapper`}><h2 className={`${p}-new-subscribers-text`}>{`${state.author} 的新订阅者`}</h2><新订阅者/>

<div className={`${p}-comment-section`}><CommentSection views={state.views}/>

</main><aside className={`${p}-sidebar`}><UpNextVideos/></一边>

}

)}导出默认视频页面

UpNextVideos 组件:

import React, { useEffect, useState, useRef, useCallback } from 'react'从'react-router-dom'导入{链接}从 'axios' 导入 axios从 '../../words' 导入 { videoQuery }从 '../../containers/api' 导入 { fetchVideos }进口 {大写第一个字母,用户名,获取随机,缩写编号} 来自'../../containers/helperFunctions'const UpNextVideos = () =>{const [p, setPrefix] = useState("videoPage")const [nextVideos, setNextVideos] = useState([])useEffect(() => {fetchUpNextVideos(15, getRandom(videoQuery))}, [])//无限滚动const 观察者 = useRef()const lastUpNextVideo = useCallback(lastVideoNode => {//重新连接观察者到最后一个帖子,包括获取数据回调如果(observer.current)observer.current.disconnect()观察者.current =新的IntersectionObserver(条目=> {const lastVideo = 条目 [0]如果(lastVideo.isIntersecting && window.innerWidth <= 1000){document.querySelector('.videoPage-show-more-button').classList.add('show')}else if (lastVideo.isIntersecting && window.innerWidth > 1000) {document.querySelector('.videoPage-show-more-button').classList.remove('show')fetchUpNextVideos(20, getRandom(videoQuery))}})如果(lastVideoNode)observer.current.observe(lastVideoNode)})const fetchUpNextVideos = async (amount, query) =>{让响应 = 等待 fetchVideos(amount, ...Array(2), query)响应 = response.data.hitsconst responseAsHtml = response.map((vid, index) => {返回 (<div className={`${p}-sidebar-grid-video-wrapper`} key={uuid()} ref={response.length === index + 1 ?lastUpNextVideo : null}><div className={`${p}-sidebar-grid-video`}><a href={`/video/id/${vid.id}-000`}><视频className={`${p}-upnext-video`}onMouseOver={event =>event.target.play()}onMouseOut={event =>event.target.pause()}src={`${vid.videos.tiny.url}#t=1`}静音 ></视频></a>

<a href={`/video/id/${vid.id}`}><h3 className={`${p}-sidebar-grid-video-title`}>{capitalizeFirstLetter(vid.tags)}</h3></a><a href={`/channel/000${vid.id}`}><p className={`${p}-sidebar-grid-video-author`}>{vid.user}</p></a><p className={`${p}-sidebar-grid-video-views-text`}>{abbreviateNumber(vid.downloads)} 次观看</p>

)})setNextVideos(prevState => ([...prevState, ...responseAsHtml]))}返回 (<div><div className={`${p}-sidebar-text-top`}><span className={`${p}-sidebar-text-upnext`}>下一个</span><span className={`${p}-sidebar-text-autoplay`}>自动播放</span>

<div className={`${p}-sidebar-grid-wrapper`}>{下一个视频}

<按钮className={`${p}-show-more-button`}onMouseDown={() =>fetchUpNextVideos(15, getRandom(videoQuery))}>展示更多

)}导出默认 UpNextVideos

我尝试过的:

解决方案

好的,我相信这个问题出在你的 VideoPage 组件上.

useEffect(() => {如果(state.loading)extractDataFromUrl()否则 handleMediaQueries()}, [state.loading]);

当组件挂载时,您只有一次 state.loading 为真.这只会处理您的 URL 一次,因此当 URL 更改时,此组件不会意识到它.

这是您当前的路线

现在假设您的 URL 的形状为/video/id/",那么您可以定义您的路由以具有参数

 

如果你用 react-router-domwithRouter HOC 您可以轻松获取id 路径参数并将其添加到效果中以重新计算所有视频数据.

默认导出withRouter(VideoPage)

withRouter 从最近的 Route 注入 locationmatchhistory 道具代码>祖先.这是获取 id 参数并在其值更新时触发效果的示例.

const VideoPage = ({ match }) =>{const { 参数 } = 匹配;useEffect(() => {/* 用新的 id */}, [params.videoId]);}

Description of problem:

Changing the id (numbers only) of this url via the link tag does not update the page (but does change the url in the adress bar). Hitting refresh afterward will show the updated page.

http://localhost:8080/video/id/7564

Right clicking to open the link in a new tab, or changing the link path to a completely different page works as expected.

My app.js file

import React from 'react'
import { Router, Route, Switch } from 'react-router-dom'
import RenderHomepage from '../components/homePage/RenderHomepage'
import RenderChannelPage from '../components/channelPage/RenderChannelPage'
import RenderVideoPage from '../components/videoPage/RenderVideoPage'
import RenderSearchPage from '../components/searchPage/RenderSearchPage'
import PageNotFound from '../components/PageNotFound'
import history from '../history'

const App = () => {
  return ( 
    <div>
      <Router history={history}>
        <Switch>
          <Route path="/" exact component={RenderHomepage} /> 
          <Route path="/channel" component={RenderChannelPage} /> 
          <Route path="/video/id" component={RenderVideoPage} /> 
          <Route path="/search" component={RenderSearchPage} /> 
          <Route path="/404" exact component={PageNotFound} />
          <Route component={PageNotFound} />
        </Switch>
      </Router>
    </div>
  )
}

export default App

Link tag in UpNextVideos component:

import React from 'react'
import { Link } from 'react-router-dom'

...
  <Link to={{pathname: vid.id}}> 
    <h3 className={`${p}-sidebar-grid-video-title`}>{capitalizeFirstLetter(vid.tags)}</h3>
  </Link>
...

How the components in question are nested:

<RenderVideoPage>
  <VideoPage>
    <UpNextVideos>

RenderVideoPage component:

import React from 'react'
import VideoPage from './VideoPage'
import Header from '../Header'
import HeaderMobile from '../HeaderMobile'
import FooterMobile from '../FooterMobile'
import ActivityFeed from '../ActivityFeed'

const RenderVideoPage = () => {
  return (
    <div className="videoPage-body">
      <HeaderMobile />
      <Header />
      <ActivityFeed page={'home'} />
      <VideoPage />
      <FooterMobile page={'video'} />
    </div>
  )
}

export default RenderVideoPage

VideoPage component:

import React, { useEffect, useState } from 'react'
import axios from 'axios'
import history from '../../history'
import handleMediaQueries from './containers/handleMediaQueries'
import setDislikes from './containers/setDislikes'

import NewSubscribers from './NewSubscribers'
import CommentSection from './CommentSection'
import UpNextVideos from './UpNextVideos'
import DescriptionBox from './DescriptionBox'
import VideoNotFound from './VideoNotFound'

import { fetchVideoFromID, fetchPictureFromID } from '../../containers/api'
import { thumbsUp, thumbsDown } from '../svgs'

import { 
  abbreviateNumber, 
  capitalizeFirstLetter, 
  randomDate } from '../../containers/helperFunctions'

const VideoPage = () => {
  const [p, setPrefix] = useState("videoPage")
  const [state, setState] = useState({
    loading: true,
    error: false
  })

  useEffect(() => {
    if (state.loading) extractDataFromUrl()
    else handleMediaQueries()
  }, [state.loading])

  const fetchVideo = async (id, picAuthorID) => {
    let response = await fetchVideoFromID(id)
    if (!response) setState(prevState => ({...prevState, error: true}))
    else mapVideoResponseToHTML(response.data.hits, picAuthorID)
  }

  const mapVideoResponseToHTML = (response, picAuthorID) => {
    let responseAsHtml = response.map(vid => {
      return {
        video: 
        <div className={`${p}-video-wrapper posRelative`} key={vid.id}>
          <a className={`${p}-pixabay-src`} href={vid.pageURL}>?</a>
          <video 
            poster="https://i.imgur.com/Us5ckqm.jpg" 
            className={`${p}-video clickable`} 
            src={vid.videos.large.url} 
            controls autoPlay>
          </video>
          <div className={`${p}-video-info-wrapper`}>  
            <div className={`${p}-video-title-box`}>
              <h1 className={`${p}-video-title`}>{capitalizeFirstLetter(vid.tags)}</h1>
              <span className={`${p}-video-views`}>{abbreviateNumber(Number(vid.downloads).toLocaleString())} views</span>
              <span className={`${p}-video-date`}>{randomDate()}</span>
            </div>
            <div className={`${p}-video-options`}>
              <div className="thumbs">
                <div className={`${p}-video-options-thumbsUp`}>{thumbsUp(20)} &nbsp; 
                  <span className={`${p}-video-options-thumbsUp-text`}>{abbreviateNumber(vid.likes)}</span>
                </div>
                <div className={`${p}-video-options-thumbsDown`}>{thumbsDown(20)} &nbsp; 
                  <span className={`${p}-video-options-thumbsDown-text`}>{setDislikes(vid.likes)}</span>
                </div>
                <div className={`${p}-video-options-likebar`}></div>
              </div>
              <span className={`${p}-video-options-share`}>Share</span>
              <span className={`${p}-video-options-save`}>Save</span>
              <span className={`${p}-video-options-ellipses`}>...</span>
            </div>
          </div>
        </div>,
        authorFollowers: vid.views,
        vidAuthorID: vid.id,
        author: picAuthorID ? 'Loading' : vid.user,
        authorAvatar: picAuthorID ? null : vid.userImageURL,
        views: vid.downloads
      }
    })
    responseAsHtml = responseAsHtml[0]
    setState(prevState => ({...prevState, ...responseAsHtml, loading: false}))
    if (picAuthorID) fetchAuthorAvatar(picAuthorID)
  }

  const extractDataFromUrl = () => {
    const currentURL = window.location.href
    const urlAsArray = currentURL.split('/')
    const urlID = urlAsArray[5].split('-')
    const videoID = urlID[0]
    const picAuthorID = urlID[1]

    // Author avatars are random except on the home page. 
    // if url isnt from homepage, then use videoID
    // if url is from homepage, send that avatarID
    if (urlID.includes('000')) {
      fetchVideo(videoID)
    } else {
      setState(prevState => ({...prevState, picAuthorID: picAuthorID}))
      fetchVideo(videoID, picAuthorID)
    }
  }

  const fetchAuthorAvatar = async (id) => {
    const response = await fetchPictureFromID(id)
    const authorName = response.data.hits[0].user
    const authorAvatar = response.data.hits[0].previewURL
    setState(prevState => ({
      ...prevState, 
      authorAvatar: authorAvatar, 
      author: capitalizeFirstLetter(authorName)
    }))
  }

  return (
    <div>
      { state.error ? <VideoNotFound /> : null}
      { state.loading === true ? null
        : 
        <div className={`${p}-page-wrapper`}>
          <main className={`${p}-main`}>
            {state.video}
            <DescriptionBox props={state} />
            <div className={`${p}-suggested-videos-mobile`}></div>

            <div className={`${p}-new-subscribers-wrapper`}>
              <h2 className={`${p}-new-subscribers-text`}>{`New Subscribers to ${state.author}`}</h2>
              <NewSubscribers />
            </div>
            <div className={`${p}-comment-section`}>
              <CommentSection views={state.views}/>
            </div>
          </main>
          <aside className={`${p}-sidebar`}>
           <UpNextVideos />
          </aside>
        </div>
      }
    </div>
  )
}

export default VideoPage

UpNextVideos component:

import React, { useEffect, useState, useRef, useCallback } from 'react'
import { Link } from 'react-router-dom'
import axios from 'axios'
import { videoQuery } from '../../words'
import { fetchVideos } from '../../containers/api'
import { 
  capitalizeFirstLetter, 
  uuid,
  getRandom,
  abbreviateNumber
} from '../../containers/helperFunctions'

const UpNextVideos = () => {
  const [p, setPrefix] = useState("videoPage")
  const [nextVideos, setNextVideos] = useState([])

  useEffect(() => {
    fetchUpNextVideos(15, getRandom(videoQuery))
  }, [])

  // INFINITE SCROLL
  const observer = useRef()
  const lastUpNextVideo = useCallback(lastVideoNode => {

    // Re-hookup observer to last post, to include fetch data callback
    if (observer.current) observer.current.disconnect()
    observer.current = new IntersectionObserver(entries => {
      const lastVideo = entries[0]
        if (lastVideo.isIntersecting && window.innerWidth <= 1000) {
          document.querySelector('.videoPage-show-more-button').classList.add('show')
        }
        else if (lastVideo.isIntersecting && window.innerWidth > 1000) {
          document.querySelector('.videoPage-show-more-button').classList.remove('show')
          fetchUpNextVideos(20, getRandom(videoQuery))
      }
    })
    if (lastVideoNode) observer.current.observe(lastVideoNode)
  })

  const fetchUpNextVideos = async (amount, query) => {
    let response = await fetchVideos(amount, ...Array(2), query)
    response = response.data.hits

    const responseAsHtml = response.map((vid, index) => {
      return (
        <div className={`${p}-sidebar-grid-video-wrapper`} key={uuid()} ref={response.length === index + 1 ? lastUpNextVideo : null}>
          <div className={`${p}-sidebar-grid-video`}>
            <a href={`/video/id/${vid.id}-000`}>
              <video 
                className={`${p}-upnext-video`} 
                onMouseOver={event => event.target.play()}
                onMouseOut={event => event.target.pause()}
                src={`${vid.videos.tiny.url}#t=1`}
                muted >
              </video>
            </a>
          </div>
          <a href={`/video/id/${vid.id}`}>
            <h3 className={`${p}-sidebar-grid-video-title`}>{capitalizeFirstLetter(vid.tags)}</h3>
          </a>
          <a href={`/channel/000${vid.id}`}>
            <p className={`${p}-sidebar-grid-video-author`}>{vid.user}</p>
          </a>
          <p className={`${p}-sidebar-grid-video-views-text`}>{abbreviateNumber(vid.downloads)} views</p>
        </div>
      )
    })
    setNextVideos(prevState => ([...prevState, ...responseAsHtml]))
  }

  return (
    <div>
      <div className={`${p}-sidebar-text-top`}>
        <span className={`${p}-sidebar-text-upnext`}>Up next</span>
        <span className={`${p}-sidebar-text-autoplay`}>Autoplay</span>
      </div>
      <div className={`${p}-sidebar-grid-wrapper`}>
        {nextVideos}
      </div> 
      <button 
        className={`${p}-show-more-button`} 
        onMouseDown={() => fetchUpNextVideos(15, getRandom(videoQuery))}>
        Show More
      </button>
    </div>
  )
}

export default UpNextVideos

What I've tried:

解决方案

Ok, I believe this issue lies in your VideoPage component.

useEffect(() => {
  if (state.loading) extractDataFromUrl()
  else handleMediaQueries()
}, [state.loading]);

You only ever have state.loading true once, when the component mounts. This only processes your URL once, so when the URL changes this component isn't aware of it.

This is your route currently

<Route path="/video/id" component={RenderVideoPage} />

now assuming your URLs are shaped "/video/id/" then you can define your route to have a parameter

 <Route path="/video/id/:videoId" component={RenderVideoPage} /> 

If you wrap this component with react-router-dom's withRouter HOC you can easily get the id path param and add it to an effect to recompute all the video data.

export default withRouter(VideoPage)

withRouter injects the location, match, and history props from the closest Route ancestor. Here's an example of getting the id param and triggering an effect when its value updates.

const VideoPage = ({ match }) => {
  const { params } = match;

  useEffect(() => { /* do something with new id */ }, [params.videoId]);

}

这篇关于react-router-dom <链接>不更新页面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆