当我在手风琴中使用插座时,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 组件,将 usernamerepo 值作为道具传递。

回购列表

<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 />
  }
];