如何在 Context API 中传递路由参数

Posted

技术标签:

【中文标题】如何在 Context API 中传递路由参数【英文标题】:How to pass route params in Context API 【发布时间】:2020-07-15 03:01:40 【问题描述】:

我正在使用上下文 API,我的上下文文件中有这个:

useEffect(() => 
    async function getSingleCountryData() 
      const result = await axios(
        `https://restcountries.eu/rest/v2/alpha/$props.match.alpha3Code.toLowerCase()`
      );
      console.log(result)
      setCountry(result.data);
    
    if (props.match) getSingleCountryData();
  , [props.match]);

在我正在使用的组件中,它不起作用,因为它不知道 props.match.alpha3Code 是什么。我怎样才能传递价值? alpha3Code 来自 URL:localhost:3000/country/asa 其中asa 是 alpha3Code,我怎样才能得到这个值?

基本上,我想做的是。我有一个我在主页上列出的国家列表。现在,我正在尝试获取有关单个国家/地区的更多信息。路由是/country/:alpha3Code,其中alpha3Code 是从API 获取的。

FWIW,这是我的完整上下文文件:

import React,  useState, createContext, useEffect  from 'react';
import axios from 'axios';

export const CountryContext = createContext();

export default function CountryContextProvider(props) 
  const [countries, setCountries] = useState([]);
  const [country, setCountry] = useState([]);

  useEffect(() => 
    const getCountryData = async () => 
      const result = await axios.get(
        'https://cors-anywhere.herokuapp.com/https://restcountries.eu/rest/v2/all'
      );
      setCountries(result.data);
    ;
    getCountryData();
  , []);

  useEffect(() => 
    async function getSingleCountryData() 
      const result = await axios(
        `https://restcountries.eu/rest/v2/alpha/$props.match.alpha3Code.toLowerCase()`
      );
      console.log(result)
      setCountry(result.data);
    
    if (props.match) getSingleCountryData();
  , [props.match]);

  return (
    <CountryContext.Provider value= countries, country >
      props.children
    </CountryContext.Provider>
  );

在我使用country 的组件中,我有: const country = useContext(CountryContext);

我知道我可以从组件本身执行此操作,但我正在学习如何使用上下文 API,因此我正在处理我的上下文中的所有 API 调用。

我正在使用的 API 是 here

Codesandbox Link

项目 Github link

【问题讨论】:

【参考方案1】:

您可以通过传递一个更新上下文状态的 setter 函数来从使用它的组件更新上下文。

export default function CountryContextProvider( children ) 
  const [countries, setCountries] = useState([]);
  const [country, setCountry] = useState([]);
  const [path, setPath] = useState('');

  useEffect(() => 
    async function getSingleCountryData() 
      const result = await axios(`your/request/for/$path`);
      setCountry(result.data);
    
    if(path) getSingleCountryData();
  , [path]);

  return (
    <CountryContext.Provider value= countries, country, setPath >
      children
    </CountryContext.Provider>
  );

现在使用 setPath 在挂载此组件后使用路由匹配更新请求端点。

const Details = ( match ) => 
  const 
    params:  alpha3Code 
   = match;
  const  country, setPath  = useContext(CountryContext);

  useEffect(() => 
    setPath(alpha3Code);
  , [alpha3Code]);

  return (
   <main>Some JSX here</main>
  );
;

export default withRouter(Details);

链接是一个有效的codesandbox implementation

【讨论】:

【参考方案2】:

在我正在使用的组件中,它不起作用,因为它不知道 props.match.alpha3Code 是什么。我怎样才能传递价值?这 alpha3Code 来自 URL:localhost:3000/country/asa 其中 asa 是 alpha3Code,我怎样才能得到这个值

我猜你的问题的根源是这个。您不知道aplha3Code 参数来自哪个。我已经深入研究了您的 GitHub 存储库以使其更清晰。

    首先,match 是 react-router 提供的术语之一。当您使用props.match, props.history, props.location 之类的东西时,您的组件必须由withRouter 包裹,这是react-router 提供的高阶组件。查看withRouter。例如,下面是 react-router 提供的 withRouter 用法:
// A simple component that shows the pathname of the current location
class ShowTheLocation extends React.Component 
  render() 
    const  match, location, history  = this.props;

    return <div>You are now at location.pathname</div>;
  


const ShowTheLocationWithRouter = withRouter(ShowTheLocation);

ShowTheLocationwithRouter HOC 包裹起来,它将所有路线道具(比赛、历史、位置...)通过道具传递给ShowTheLocation。然后在ShowTheLocation 中,您可以使用props.match 之类的东西。够清楚吗?

回到你的问题!您还没有通过withRouter 包装任何组件,是吗?坚持下去,玩得开心!你很快就会明白的!

另外,请注意您必须将您的组件放在BrowserRouter 下才能使用 react-router 的东西

    如果你想使用 Hooks,请看看这个超级有用的:

    https://usehooks.com/useRouter/

它将所有useParams, useLocation, useHistory, and use useRouteMatch 挂钩包装成一个useRouter,只公开我们需要的数据和方法。然后,例如,在你的组件内部,这样做:

import  useRouter  from "./myCustomHooks";

const ShowMeTheCode = () =>  
   const router = useRouter();
   return <div>This is my alpha3Code: router.math.params.alpha3Code</div>;


Peoray 回复中的更新 1:

这是出现问题的地方: https://github.com/peoray/where-in-the-world/blob/cb09871fefb2f58f5cf0a4f1db3db2cc5227dfbe/src/pages/Details.js#L6

你应该避免像这样直接调用useContext()。看看我下面的例子:

// CountryContext.js
import  useContext, createContext  from "react";

const CountryContext = createContext();

export const useCountryContext = () => useContext(CountryContext);

相反,您应该使用上面的useCountryContext 之类的自定义钩子来包装它。然后,在您的 Details 组件中,导入它并执行以下操作:

import React, from 'react';
import  useCountryContext  from '../contexts/CountryContext';

const Details = (props) => 
  const  country  = useCountryContext();
  ...


Peoray 回复中的更新 2:

虽然我已经提前给你说了,但我只是觉得你没有付出足够的努力去完成我所说的。

另外,请注意您必须将您的组件放在 BrowserRouter 能够使用 react-router 的东西

在您的codesandbox 中,它显示Cannot read property 'match' of undefined 错误。好的,正如我上面所说,您还没有将ContextCountryProvider 移动到BrowserRouter 下以获得useRouter 工作。

我已经给你修好了,弹出了这个界面,请到updated codesanbox here查看。您将在 App.js 文件中获得所需的内容。

虽然它仍然会在那里抛出一些 Axios 错误,但我认为我的工作已经完成。剩下的就看你自己了。

【讨论】:

我尝试了您的建议,但没有成功。得到TypeError: useContext(...) is undefined。不确定,如果我做得对。如果你想看看,我更新了 repo :) 首先,我认为你对 React 和 react-router 还是很陌生。因此,您应该删除 Context API,并首先使用 react-router 之类的东西,例如 withRouter HOC。一旦你习惯了 React 世界,这就是 Context API、Hooks 来拯救的时候。如果您觉得我的建议有用,请点赞并标记接受! 您好,我确实查看了您的更新。我想我在这里得到它。等待我的更新! @Peoray 嘿,我已经更新了我的答案。详情请查看Update 1 from Peoray's reply。我想现在一切都好。如果您觉得有用,请不要忘记点赞并标记接受! 你试过这个并且成功了吗?因为之前的方法对Home.js 文件非常有效。另外,我对 React 并不陌生 :),只是对更新不熟悉,我的日常工作是 Vue,所以我正在努力恢复使用 React 的速度 :)【参考方案3】:

您可以使用useParams 钩子在上下文提供程序中获取所需的一切。 Docs

类似这样的:

    在您的 Provider 组件所在的文件中导入 useParams 在您的 CountryContextProvider 中将其添加到组件的顶部:
const  alpha3Code  = useParams();
    更新需要props.matchuseEffect
useEffect(() => 
    async function getSingleCountryData() 
      const result = await axios(
        `https://restcountries.eu/rest/v2/alpha/$alpha3Code.toLowerCase()`
      );
      console.log(result)
      setCountry(result.data);
    
    if (alpha3Code) getSingleCountryData(); // or if you need `match` - do not destructure useParams()
  , [alpha3Code]);

【讨论】:

如果我在 Provider 组件所在的文件中导入 useParams,在我的例子中是:src/pages/Details.js,我得到一个已定义但未使用的文件,我该如何使用它?在我的仓库中,我使用的是props.match,但什么也没发生,仍然返回空 它在第 2 步中说:“在您的 CountryContextProvider 中将其添加到 ...” 对不起,我不明白你的意思:( 这意味着它应该在这个大括号内:export default function CountryContextProvider(props) ...useState 行之前或之后 我收到了一个:TypeError: useContext(...) is undefined

以上是关于如何在 Context API 中传递路由参数的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 useEffect 和 Context API 来检查用户是不是登录并保护路由?

在组件之间路由时如何保持 React 新的 Context API 状态?

如何使用 Context API 将单个 Socket IO 实例传递给页面?

如何使用角度路径解析器从api解析数据

api的url规则设计,带参数的路由

在 React.js 的父组件中使用 react-router 时,如何使用 react context API 将数据从父组件传递到子组件?