我正在使用 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>
(正确),但该组件仍显示有关上一个项目的信息。
我已经尝试了几种修复方法,例如 using navigate("/projects/" + project._id, {replace: true});
,但没有任何效果。唯一的方法似乎是使用 href,但我希望这个项目是一个单页网络应用程序。所以我只需要导航而不重新加载整个文档。
--- 编辑: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);
});
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句