当我在手风琴中使用插座时,useEffect 中的 axios 请求一直在执行
When I use outlet in accordion, axios request in useEffect keeps executing
因为我的outlet是放在react-bootstrap的手风琴体里面的,
所以当我进入子路由时,一直执行子路由的useEffect内容。直到手风琴的主体完全打开,想知道有没有办法修复它。
问题是插座好像不能放在accordion.body,希望有解决办法,谢谢! ! !
这是父路由:
import React from 'react'
import { useParams, Link, Outlet } from 'react-router-dom'
import request from '../common/utils'
import { Spinner, Accordion } from 'react-bootstrap'
export default function ReposList() {
let user = useParams()
let [userData, setUserData] = React.useState(null)
let [isLoading, setLoading] = React.useState(true)
let [isPage, setPage] = React.useState(2)
function getAPI(per_page, page) {
return request({
url: `users/${user.username}/repos`,
params: {
per_page,
page
}
})
}
//下拉加載
function handleScroll(e) {
if (e.target.clientHeight + parseInt(e.target.scrollTop)
=== e.target.scrollHeight) {
getAPI(10, isPage).then((res) => {
setUserData([...userData, ...res.data])
})
setPage(isPage + 1)
}
}
React.useEffect(() => {
const fetchData = async () => {
const result = await getAPI(10, 1)
setUserData(result.data)
//console.log(res.data)
setLoading(false)
console.log(1)
}
fetchData()
}, [])
if (isLoading) {
return (
<Spinner animation="border" role="status">
<span className="visually-hidden">Loading...</span>
</Spinner>
)
}
//手風琴要包著map生成的item,不然item之間沒有對摺效果
return (
<div className='repos' onScroll={handleScroll} >
<Accordion>
{userData.map((data) => (
// key要放在元素的最上方,不然會報錯。
<Accordion.Item eventKey={data.id} key={data.id}>
<Accordion.Header as={Link} to={data.name}>
{data.name}{ ' '}{data.language == null? '': `/ ${data.language}`}
</Accordion.Header>
<Accordion.Body>
<Outlet/>
</Accordion.Body>
</Accordion.Item>
))}
</Accordion>
</div>
)
}
这是子路由:
import React from 'react'
import { useParams } from 'react-router-dom'
import { Spinner , Table } from 'react-bootstrap'
import request from '../common/utils'
export default function Repo() {
let user = useParams()
let [userData, setUserData] = React.useState(null)
let [isLoading , setLoading] = React.useState(true)
//console.log(user)
function getAPI(per_page,page) {
return request({
url: `repos/${user.username}/${user.repo}`,
params:{
per_page,
page
}
})
}
React.useEffect(() => {
const fetchData = async () => {
const result = await getAPI()
setUserData(result.data)
//console.log(res.data)
setLoading(false)
console.log(1)
}
fetchData()
}, [user])
if (isLoading) {
return (
<Spinner animation="border" role="status">
<span className="visually-hidden">Loading...</span>
</Spinner>
)
}
return(
<Table striped bordered hover size="sm"
className='repoTable'>
<thead>
<tr>
<th>full_name</th>
<th>description</th>
<th>stargazers_count</th>
<th>url</th>
</tr>
</thead>
<tbody>
<tr>
<td>{userData.full_name}</td>
<td>{userData.description == null? 'null' : userData.description}</td>
<td>{userData.stargazers_count}</td>
<td>
<a href={userData.svn_url }target="_blank">
Go!
</a>
</td>
</tr>
</tbody>
</Table>
)
}
完整代码和演示
https://github.com/superrjohn/GithubRestApi
希望有人能帮忙,谢谢!
问题
尝试在映射的手风琴主体中呈现多个 Outlet
组件是一个问题。 ReposList
作为父路由的组件应该只为嵌套的子路由呈现一个 Outlet
。只有一条 "/user/:username/repos/:repo"
路线。
解决方案
我建议直接在手风琴主体中渲染 Repo
组件,将 username
和 repo
值作为道具传递。
回购列表
<Accordion>
{userData.map((data) => (
<Accordion.Item eventKey={data.id} key={data.id}>
<Accordion.Header as={Link} to={data.name}>
{data.name}
{" "}
{data.language == null ? "" : `/ ${data.language}`}
</Accordion.Header>
<Accordion.Body>
<Repo {...{ username, repo: data.name }} /> // Render Repo and pass props
</Accordion.Body>
</Accordion.Item>
))}
</Accordion>
回购
export default function Repo({ username, repo }) { // access props
const [userData, setUserData] = React.useState(null);
const [isLoading, setLoading] = React.useState(true);
React.useEffect(() => {
function getAPI(per_page, page) {
return request({
url: `repos/${username}/${repo}`,
params: {
per_page,
page
}
});
}
const fetchData = async () => {
const result = await getAPI();
setUserData(result.data);
setLoading(false);
};
fetchData();
}, [repo, username]);
if (isLoading) {
return (
<Spinner animation="border" role="status">
<span className="visually-hidden">Loading...</span>
</Spinner>
);
}
return (
<Table striped bordered hover size="sm" className="repoTable">
<thead>
<tr>
<th>full_name</th>
<th>description</th>
<th>stargazers_count</th>
<th>url</th>
</tr>
</thead>
<tbody>
<tr>
<td>{userData.full_name}</td>
<td>
{userData.description == null ? "null" : userData.description}
</td>
<td>{userData.stargazers_count}</td>
<td>
<a href={userData.svn_url} target="_blank">
Go!
</a>
</td>
</tr>
</tbody>
</Table>
);
}
src/router/index.js
删除呈现 Repo
组件的嵌套路由,并向父路由添加尾随通配符 "*"
匹配器。
const routes = [
{
path: "/",
element: <NavBarExample />,
children: [
{
index: true,
element: <Home />
},
{
path: "user",
element: <Home />,
children: [
{
path: ":username",
element: <UserName />,
children: [
{
path: "repos/*",
element: <ReposList />,
}
]
}
]
},
{
path: "About",
element: <About />
},
{
path: "Contact",
element: <Contact />
}
]
},
{
path: "*",
element: <NoMatch />
}
];
因为我的outlet是放在react-bootstrap的手风琴体里面的, 所以当我进入子路由时,一直执行子路由的useEffect内容。直到手风琴的主体完全打开,想知道有没有办法修复它。
问题是插座好像不能放在accordion.body,希望有解决办法,谢谢! ! !
这是父路由:
import React from 'react'
import { useParams, Link, Outlet } from 'react-router-dom'
import request from '../common/utils'
import { Spinner, Accordion } from 'react-bootstrap'
export default function ReposList() {
let user = useParams()
let [userData, setUserData] = React.useState(null)
let [isLoading, setLoading] = React.useState(true)
let [isPage, setPage] = React.useState(2)
function getAPI(per_page, page) {
return request({
url: `users/${user.username}/repos`,
params: {
per_page,
page
}
})
}
//下拉加載
function handleScroll(e) {
if (e.target.clientHeight + parseInt(e.target.scrollTop)
=== e.target.scrollHeight) {
getAPI(10, isPage).then((res) => {
setUserData([...userData, ...res.data])
})
setPage(isPage + 1)
}
}
React.useEffect(() => {
const fetchData = async () => {
const result = await getAPI(10, 1)
setUserData(result.data)
//console.log(res.data)
setLoading(false)
console.log(1)
}
fetchData()
}, [])
if (isLoading) {
return (
<Spinner animation="border" role="status">
<span className="visually-hidden">Loading...</span>
</Spinner>
)
}
//手風琴要包著map生成的item,不然item之間沒有對摺效果
return (
<div className='repos' onScroll={handleScroll} >
<Accordion>
{userData.map((data) => (
// key要放在元素的最上方,不然會報錯。
<Accordion.Item eventKey={data.id} key={data.id}>
<Accordion.Header as={Link} to={data.name}>
{data.name}{ ' '}{data.language == null? '': `/ ${data.language}`}
</Accordion.Header>
<Accordion.Body>
<Outlet/>
</Accordion.Body>
</Accordion.Item>
))}
</Accordion>
</div>
)
}
这是子路由:
import React from 'react'
import { useParams } from 'react-router-dom'
import { Spinner , Table } from 'react-bootstrap'
import request from '../common/utils'
export default function Repo() {
let user = useParams()
let [userData, setUserData] = React.useState(null)
let [isLoading , setLoading] = React.useState(true)
//console.log(user)
function getAPI(per_page,page) {
return request({
url: `repos/${user.username}/${user.repo}`,
params:{
per_page,
page
}
})
}
React.useEffect(() => {
const fetchData = async () => {
const result = await getAPI()
setUserData(result.data)
//console.log(res.data)
setLoading(false)
console.log(1)
}
fetchData()
}, [user])
if (isLoading) {
return (
<Spinner animation="border" role="status">
<span className="visually-hidden">Loading...</span>
</Spinner>
)
}
return(
<Table striped bordered hover size="sm"
className='repoTable'>
<thead>
<tr>
<th>full_name</th>
<th>description</th>
<th>stargazers_count</th>
<th>url</th>
</tr>
</thead>
<tbody>
<tr>
<td>{userData.full_name}</td>
<td>{userData.description == null? 'null' : userData.description}</td>
<td>{userData.stargazers_count}</td>
<td>
<a href={userData.svn_url }target="_blank">
Go!
</a>
</td>
</tr>
</tbody>
</Table>
)
}
完整代码和演示 https://github.com/superrjohn/GithubRestApi
希望有人能帮忙,谢谢!
问题
尝试在映射的手风琴主体中呈现多个 Outlet
组件是一个问题。 ReposList
作为父路由的组件应该只为嵌套的子路由呈现一个 Outlet
。只有一条 "/user/:username/repos/:repo"
路线。
解决方案
我建议直接在手风琴主体中渲染 Repo
组件,将 username
和 repo
值作为道具传递。
回购列表
<Accordion>
{userData.map((data) => (
<Accordion.Item eventKey={data.id} key={data.id}>
<Accordion.Header as={Link} to={data.name}>
{data.name}
{" "}
{data.language == null ? "" : `/ ${data.language}`}
</Accordion.Header>
<Accordion.Body>
<Repo {...{ username, repo: data.name }} /> // Render Repo and pass props
</Accordion.Body>
</Accordion.Item>
))}
</Accordion>
回购
export default function Repo({ username, repo }) { // access props
const [userData, setUserData] = React.useState(null);
const [isLoading, setLoading] = React.useState(true);
React.useEffect(() => {
function getAPI(per_page, page) {
return request({
url: `repos/${username}/${repo}`,
params: {
per_page,
page
}
});
}
const fetchData = async () => {
const result = await getAPI();
setUserData(result.data);
setLoading(false);
};
fetchData();
}, [repo, username]);
if (isLoading) {
return (
<Spinner animation="border" role="status">
<span className="visually-hidden">Loading...</span>
</Spinner>
);
}
return (
<Table striped bordered hover size="sm" className="repoTable">
<thead>
<tr>
<th>full_name</th>
<th>description</th>
<th>stargazers_count</th>
<th>url</th>
</tr>
</thead>
<tbody>
<tr>
<td>{userData.full_name}</td>
<td>
{userData.description == null ? "null" : userData.description}
</td>
<td>{userData.stargazers_count}</td>
<td>
<a href={userData.svn_url} target="_blank">
Go!
</a>
</td>
</tr>
</tbody>
</Table>
);
}
src/router/index.js
删除呈现 Repo
组件的嵌套路由,并向父路由添加尾随通配符 "*"
匹配器。
const routes = [
{
path: "/",
element: <NavBarExample />,
children: [
{
index: true,
element: <Home />
},
{
path: "user",
element: <Home />,
children: [
{
path: ":username",
element: <UserName />,
children: [
{
path: "repos/*",
element: <ReposList />,
}
]
}
]
},
{
path: "About",
element: <About />
},
{
path: "Contact",
element: <Contact />
}
]
},
{
path: "*",
element: <NoMatch />
}
];