繁体   English   中英

React Router v6 更改 URL 但不呈现

[英]React Router v6 changes URL but doesn't render

我正在使用 React Router V6 开发 web 应用程序。 每个用户都有几个项目,根据 React Router 文档,我是这样声明路由的:

<Routes>
    <Route index path='/' element={<GeneralOverview user={props.user}> </GeneralOverview>}/>
    ... 
    <Route path='/users/:userId' element={<UserPage></UserPage>} />
    <Route path='/create-project' element={<CreateProject></CreateProject>} />
    <Route path='/projects/*'>
        <Route path=':projectId' element={<ProjectOverview />} />
    <Route path=':projectId/repos' element={<ProjectRepos></ProjectRepos>} />
    <Route path=':projectId/issues' element={<ProjectIssues></ProjectIssues>}/>
    <Route path=':projectId/issues/:issueId' element={<IssuePage></IssuePage>}></Route>
    ... 
</Routes>

我构建了一个侧边栏,它显示了所有用户项目的列表,其中包含可单击的元素以导航到项目页面。 这是侧边栏组件的子组件,呈现项目列表:

let ProjectsList = props => {
    let navigate = useNavigate();
    return props.projects.map(project =>
    <CNavItem key={project._id} href='#' onClick={event => {
        event.preventDefault();
        navigate("/projects/" + project._id);
    }}>
        {project.name}
    </CNavItem>)
}

单击该项目,URL 从/projects/<old-id>更改为/projects/<new-id> (正确),但该组件仍显示有关先前项目的信息。

我已经尝试了几种修复方法,比如使用navigate("/projects/" + project._id, {replace: true}); ,但没有任何效果。 唯一的方法似乎是使用 href,但我希望这个项目是单页 web 应用程序。 所以我只需要在不重新加载整个文档的情况下进行导航。

--- 编辑:ProjectOverview 组件代码 ---

根据要求,这里是项目概览代码,省略了一些“无用”的部分。

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { Link, Navigate, useLocation, useNavigate, useParams } from "react-router-dom";
import { CContainer, CAlert, CCard, CRow, CCol, CHeader, CHeaderBrand,
    CCardBody, CCardHeader, CCardTitle, CButton, CModal, CModalHeader, 
    CModalTitle, CModalBody, CFormInput, CModalFooter, CCardText, CFormLabel, 
    CFormSelect, CBadge, CHeaderDivider, CDropdownDivider} from '@coreui/react';
import CIcon from '@coreui/icons-react';
import * as icon from '@coreui/icons';
import { CChart } from '@coreui/react-chartjs';


const ProjectOverview = props => {
    let { projectId } = useParams();
    let navigate = useNavigate();
    
    let [project, setProject] = useState();
    let [owner, setOwner] = useState();
    let [repos, setRepos] = useState();

    /**
     * some state variables for errors, handlers and data distribution
     */

    useEffect(() => {
        axios.get('http://localhost:4000/projects/' + projectId, {withCredentials:true}).then(res => {
            setProject(res.data);
            axios.get('http://localhost:4000/projects/' + projectId + '/owner', {withCredentials:true}).then(res => {
                setOwner(res.data);
                axios.get('http://localhost:4000/projects/' + projectId + '/repos', {withCredentials:true}).then(res => {
                    setRepos(res.data);
                    axios.get('http://localhost:4000/projects/' + projectId + '/issues', {withCredentials:true}).then(res => {
                        /**
                         * distributing data for charts
                         */
                        let repoDistrubution = [0,0,0,0];
                        let csetDistribution = [0,0,0,0];
                        let cSum = 0, rSum = 0;
                        for (const issue of res.data) {
                            if (issue.repoId) {
                                switch (issue.status) {
                                    case 'open': repoDistrubution[0]++; break;
                                    case 'ignored': repoDistrubution[1]++; break;
                                    case 'working on': repoDistrubution[2]++; break;
                                    case 'fixed': repoDistrubution[3]++; break;
                                }
                                rSum++;
                            } else {
                                switch (issue.status) {
                                    case 'open': csetDistribution[0]++; break;
                                    case 'ignored': csetDistribution[1]++; break;
                                    case 'working on': csetDistribution[2]++; break;
                                    case 'fixed': csetDistribution[3]++; break;
                                }
                                cSum++;
                            }
                        }
                        setCsetIssueDistribution(csetDistribution);
                        setReposIssueDistribution(repoDistrubution);
                        setCsetSum(cSum); setReposSum(rSum);
                    })
                });
            });
        }).catch(err => {
            if (err.status === 404) {
                setProjectNotFound(true);
            }
            console.log(err)
        });
        axios.get('https://api.github.com/user/repos', {headers: {
            'Authorization': 'Bearer ' + localStorage.getItem('github_token')
        }, params: {'per_page': 100, 'page':1}}
        ).then(res => {
            let repos = ['select one of your repos'];
            for (const repo of res.data) {
                repos.push(repo.full_name)
            }
            setAvailableRepos(repos);
        }).catch(err => console.log(err));
        axios.get('http://localhost:4000/avaiable-assessment', {withCredentials: true}).then(res => {
            let assessments = []
            for (const assessment of res.data) {
                assessments.push({
                    id: assessment.Id,
                    name: assessment.Assessment_Name
                })
            }
            setAvaiableAssessments(assessments);
        })
    }, []);

    /**
     * some handlers
     */

    return <CContainer>

        {project && repos ? (
            <div>
                <CHeader className="" style={{paddingTop:"0rem"}}>
                    <CCol>
                        <CRow>
                        <CCol md='auto' style={{borderRight: '1px solid grey'}}>   
                        <CHeaderBrand >Project name: {project.name}</CHeaderBrand>
                        </CCol>
                        <CCol md='auto' style={{borderRight: '1px solid grey', marginLeft:'1rem'}}>
                        <CHeaderBrand>Owner: {owner && owner.username}</CHeaderBrand>
                        </CCol>
                        <CCol md='auto' style={{marginLeft:'1rem'}}>
                        <CHeaderBrand>Status: {project.status}</CHeaderBrand>
                        </CCol>
                        </CRow>
                    </CCol>
                    <CCol md='auto' style={{margileft:'1rem', marginRight:'1rem'}}>
                        // buttons and handlers
                    </CCol>
                </CHeader><br/>
                <CRow>
                    <CCol>
                        <CCard>
                            <CCardBody>
                                <CCardTitle>Details</CCardTitle>
                                <CCardText>
                                    Description: {project.description}
                                </CCardText>
                                <CCardText>
                                    <CBadge style={{marginRight:'1rem'}} className="bg-dark">{project.issues.length}</CBadge> 
                                    <Link to={'/projects/' + project._id + '/issues'}
                                        state={{project: project}}
                                        style={{ textDecoration: 'none'}}>
                                        Issues
                                    </Link>
                                </CCardText>
                            </CCardBody>
                        </CCard>
                    </CCol>
                    <CCol>
                        <CCard>
                            <CCardBody>
                                <CCardTitle>
                                    Repositories
                                    <CButton onClick={() => setAddingRepo(true)}>
                                        <CIcon icon={icon.cilPlus}></CIcon> 
                                    </CButton> 
                                </CCardTitle>
                                <RepoList setProject={setProject} project={project} repos={repos}></RepoList>
                                // just prints the list of repos and handles thier removal
                            </CCardBody>
                        </CCard>
                    </CCol>
                    <CCol>
                        <CCard>
                            <CCardBody>
                                <CCardTitle>
                                    Collaborators
                                    <CButton onClick={() => setAddingCollab(true)}>
                                        <CIcon icon={icon.cilPlus}></CIcon> 
                                    </CButton> 
                                </CCardTitle>
                                <CollaboratorsList setProject={setProject} project={project} ></CollaboratorsList>
                                // just prints the list of collaborators and handles thier removal
                            </CCardBody>
                        </CCard>
                    </CCol>
                </CRow>
            </div>
        ) : (
            <div> skeleton (TODO)</div>
        ) }
    </CContainer>
}

export default ProjectOverview;

当渲染ProjectOverview组件并且路由路径发生变化并且projectId发生变化时,不会再次触发useEffect挂钩来获取任何数据。

projectId添加到useEffect挂钩的依赖项数组,这样当projectId值更改时,效果会再次运行。

const ProjectOverview = (props) => {
  const { projectId } = useParams();
  ...

  useEffect(() => {
    axios
      .get("http://localhost:4000/projects/" + projectId, {
        withCredentials: true
      })
      .then((res) => {
        setProject(res.data);
        axios
          .get("http://localhost:4000/projects/" + projectId + "/owner", {
            withCredentials: true
          })
          .then((res) => {
            setOwner(res.data);
            axios
              .get("http://localhost:4000/projects/" + projectId + "/repos", {
                withCredentials: true
              })
              .then((res) => {
                setRepos(res.data);
                axios
                  .get(
                    "http://localhost:4000/projects/" + projectId + "/issues",
                    { withCredentials: true }
                  )
                  .then((res) => {
                    ...
                  });
              });
          });
      })
      .catch((err) => {
        ...
      });
    ...
  }, [projectId]);

此外,仅作为建议,您应该展平 Promise 链。 创建了 Promise 条链来解决称为“嵌套地狱”的问题,该问题涉及嵌套异步回调,您没有逃脱嵌套问题。

例子:

const options = { withCredentials: true };
axios
  .get("http://localhost:4000/projects/" + projectId, options)
  .then((res) => {
    setProject(res.data);
    return axios.get(
      "http://localhost:4000/projects/" + projectId + "/owner",
      options
    );
  })
  .then((res) => {
    setOwner(res.data);
    return axios.get(
      "http://localhost:4000/projects/" + projectId + "/repos",
      options
    );
  })
  .then((res) => {
    setRepos(res.data);
    return axios.get(
      "http://localhost:4000/projects/" + projectId + "/issues",
      options
    );
  })
  .then((res) => {
    /**
     * distributing data for charts
     */
    const repoDistrubution = [0, 0, 0, 0];
    const csetDistribution = [0, 0, 0, 0];
    let cSum = 0;
    let rSum = 0;
    for (const issue of res.data) {
      ...
    }
    setCsetIssueDistribution(csetDistribution);
    setReposIssueDistribution(repoDistrubution);
    setCsetSum(cSum);
    setReposSum(rSum);
  })
  .catch((err) => {
    if (err.status === 404) {
      setProjectNotFound(true);
    }
    console.log(err);
  });

存在<Routes>没有结束标记的问题

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM