I'm working to a web app with React Router V6. Each user has several projects, according with React Router documentation, I declared routes in this way:
<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>
I've build a sidebar, which shows a list of all user's projects with clickable elements to navigate to the project page. This is a child of the sidebar component, that renders a list of projects:
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>)
}
Clicking the item, the URL changes from /projects/<old-id> to /projects/<new-id> (correct), but the component is still showing information about the previous project.
I've already tried several fix, like using navigate("/projects/" + project._id, {replace: true}); , but nothing worked. The only way seems to be using href, but I want this project to be a single page web app. So I just need to navigate without reloading the entire document.
--- edit: ProjectOverview component code ---
As requested, here's project overview code, omitted some "useless" parts.
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;
ProjectOverviewcomponent. Would you mind posting the code from there? If it's not rendering the new information correctly, it may be an issue with dependency array in useEffect or really any other hook that may have stale info.