如何使用React-Router-DOM v5的渲染属性(render props)与TypeScript。

huangapple go评论73阅读模式
英文:

How to use react-router-dom v5 render props using Typscript

问题

如何在Typescript中使用Routerender属性?

您可以在Typescript中使用Routerender属性,但需要为render函数提供适当的类型定义。以下是如何做的示例:

首先,确保您的React Router版本是5.x或更高版本,因为在React Router 6中,render属性已被移除,而是使用element属性。

import React from 'react';
import { BrowserRouter, Route, RouteProps } from 'react-router-dom';

// 先为RouteProps类型添加一个render函数的定义
interface CustomRouteProps extends RouteProps {
  render: (props: any) => React.ReactNode;
}

// 然后,创建一个自定义的Route组件,使用刚刚定义的CustomRouteProps类型
const CustomRoute: React.FC<CustomRouteProps> = ({ render, ...rest }) => {
  return <Route {...rest} render={render} />;
};

// 现在,您可以在应用程序中使用CustomRoute组件了
function App() {
  return (
    <BrowserRouter>
      <CustomRoute
        path="/"
        render={props => (
          <>
            <Header {...props} />
            <Routes />
            <Footer />
          </>
        )}
      />
    </BrowserRouter>
  );
}

export default App;

通过上述示例,您可以将render属性的类型定义为接受一些props并返回React节点的函数,并且在应用程序中使用CustomRoute组件来代替原始的Route组件,以便能够使用render属性。

希望这可以帮助您在Typescript中使用Routerender属性。如果您有任何其他问题,请随时提问。

英文:

I tried to convert a React app from Javascript to Typescript from code in youtube tutorial.

import &#39;swiper/swiper.min.css&#39;;
import &#39;./assets/boxicons-2.0.7/css/boxicons.min.css&#39;;
import &#39;./App.scss&#39;;
import { BrowserRouter, Route } from &#39;react-router-dom&#39;;
import Header from &#39;./components/header/Header&#39;;
import Footer from &#39;./components/footer/Footer&#39;;
import Routes from &#39;./config/Routes&#39;;

function App() {
  return (
    &lt;BrowserRouter&gt;
      &lt;Route
        render={props =&gt; (
          &lt;&gt;
            &lt;Header {...props} /&gt;
            &lt;Routes /&gt;
            &lt;Footer /&gt;
          &lt;/&gt;
        )}
      /&gt;
    &lt;/BrowserRouter&gt;
  );
}

export default App;

but I got error at this part
如何使用React-Router-DOM v5的渲染属性(render props)与TypeScript。

This is my Header code

import { useRef, useEffect } from &#39;react&#39;;
import { Link, useLocation } from &#39;react-router-dom&#39;;
import &#39;./header.scss&#39;;
import logo from &#39;../../assets/tmovie.png&#39;;

const headerNav = [
    {
        display: &#39;Home&#39;,
        path: &#39;/&#39;
    },
    {
        display: &#39;Movies&#39;,
        path: &#39;/movie&#39;
    },
    {
        display: &#39;TV Series&#39;,
        path: &#39;/tv&#39;
    }
];

const Header = () =&gt; {

    const { pathname } = useLocation();
    const headerRef:any = useRef(null);

    const active = headerNav.findIndex(e =&gt; e.path === pathname);

    useEffect(() =&gt; {
        const shrinkHeader = () =&gt; {
            if (document.body.scrollTop &gt; 100 || document.documentElement.scrollTop &gt; 100) {
                headerRef.current.classList.add(&#39;shrink&#39;);
            } else {
                headerRef.current.classList.remove(&#39;shrink&#39;);
            }
        }
        window.addEventListener(&#39;scroll&#39;, shrinkHeader);
        return () =&gt; {
            window.removeEventListener(&#39;scroll&#39;, shrinkHeader);
        };
    }, []);

    return (
        &lt;div ref={headerRef} className=&quot;header&quot;&gt;
            &lt;div className=&quot;header__wrap container&quot;&gt;
                &lt;div className=&quot;logo&quot;&gt;
                    &lt;img src={logo} alt=&quot;&quot; /&gt;
                    &lt;Link to=&quot;/&quot;&gt;tMovies&lt;/Link&gt;
                &lt;/div&gt;
                &lt;ul className=&quot;header__nav&quot;&gt;
                    {
                        headerNav.map((e, i) =&gt; (
                            &lt;li key={i} className={`${i === active ? &#39;active&#39; : &#39;&#39;}`}&gt;
                                &lt;Link to={e.path}&gt;
                                    {e.display}
                                &lt;/Link&gt;
                            &lt;/li&gt;
                        ))
                    }
                &lt;/ul&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
}

export default Header;

I use some package

@testing-library/jest-dom: 5.16.5

@testing-library/react: 14.0.0

@testing-library/user-event: 14.4.3

axios: 1.4.0

prop-types: 15.8.1

query-string: 8.1.0

react: 18.2.0

react-dom: 18.2.0

react-router-dom: 5.3.0

sass: 1.62.1

swiper: 6.8.4

More detail you can see my json
Here my package.json

{
  &quot;name&quot;: &quot;dummy&quot;,
  &quot;private&quot;: true,
  &quot;version&quot;: &quot;0.0.0&quot;,
  &quot;type&quot;: &quot;module&quot;,
  &quot;scripts&quot;: {
    &quot;dev&quot;: &quot;vite&quot;,
    &quot;build&quot;: &quot;tsc &amp;&amp; vite build&quot;,
    &quot;lint&quot;: &quot;eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0&quot;,
    &quot;preview&quot;: &quot;vite preview&quot;
  },
  &quot;dependencies&quot;: {
    &quot;@testing-library/jest-dom&quot;: &quot;^5.16.5&quot;,
    &quot;@testing-library/react&quot;: &quot;^14.0.0&quot;,
    &quot;@testing-library/user-event&quot;: &quot;^14.4.3&quot;,
    &quot;axios&quot;: &quot;^1.4.0&quot;,
    &quot;prop-types&quot;: &quot;^15.8.1&quot;,
    &quot;query-string&quot;: &quot;^8.1.0&quot;,
    &quot;react&quot;: &quot;^18.2.0&quot;,
    &quot;react-dom&quot;: &quot;^18.2.0&quot;,
    &quot;react-router-dom&quot;: &quot;^5.3.0&quot;,
    &quot;sass&quot;: &quot;^1.62.1&quot;,
    &quot;swiper&quot;: &quot;^6.8.4&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@types/react&quot;: &quot;^18.0.37&quot;,
    &quot;@types/react-dom&quot;: &quot;^18.0.11&quot;,
    &quot;@types/react-router-dom&quot;: &quot;^5.3.3&quot;,
    &quot;@typescript-eslint/eslint-plugin&quot;: &quot;^5.59.0&quot;,
    &quot;@typescript-eslint/parser&quot;: &quot;^5.59.0&quot;,
    &quot;@vitejs/plugin-react&quot;: &quot;^4.0.0&quot;,
    &quot;eslint&quot;: &quot;^8.38.0&quot;,
    &quot;eslint-plugin-react-hooks&quot;: &quot;^4.6.0&quot;,
    &quot;eslint-plugin-react-refresh&quot;: &quot;^0.3.4&quot;,
    &quot;typescript&quot;: &quot;^5.0.2&quot;,
    &quot;vite&quot;: &quot;^4.3.9&quot;
  }
}

How to use Route render prop in Typescript?

答案1

得分: 1

`Header`组件实际上没有使用任何props

```tsx
const Header = () => { // &lt;-- 没有PROPS
  const { pathname } = useLocation();
  const headerRef:any = useRef(null);

  const active = headerNav.findIndex(e => e.path === pathname);

  ...

Header使用了替代了旧的路由props的React hooks,特别是useLocation hook,用于访问先前的props.location prop值。现在使用React hooks是首选方法。

由于没有使用props,您应该清理路由并删除传递路由props的部分。

function App() {
  return (
    &lt;BrowserRouter&gt;
      &lt;Route
        render={() => (
          &lt;&gt;
            &lt;Header /&gt;
            &lt;Routes /&gt;
            &lt;Footer /&gt;
          &lt;/&gt;
        )}
      /&gt;
    &lt;/BrowserRouter&gt;
  );
}

或者作为一个小的优化,直接渲染HeaderRoutesFooter,因为它们由一个无路径的路由渲染并且无条件地渲染。

function App() {
  return (
    &lt;BrowserRouter&gt;
      &lt;Header /&gt;
      &lt;Routes /&gt;
      &lt;Footer /&gt;
    &lt;/BrowserRouter&gt;
  );
}

<details>
<summary>英文:</summary>

The `Header` component isn&#39;t actually consuming any props:

```tsx
const Header = () =&gt; { // &lt;-- NO PROPS
  const { pathname } = useLocation();
  const headerRef:any = useRef(null);

  const active = headerNav.findIndex(e =&gt; e.path === pathname);

  ...

Header is using the React hooks that have supplanted the older route props, in this case, specifically the useLocation hook to access what was previously the props.location prop value. Using the React hooks is now the preferred method.

Since no props consumed you should clean up the route and remove the passing through of route props.

function App() {
  return (
    &lt;BrowserRouter&gt;
      &lt;Route
        render={() =&gt; (
          &lt;&gt;
            &lt;Header /&gt;
            &lt;Routes /&gt;
            &lt;Footer /&gt;
          &lt;/&gt;
        )}
      /&gt;
    &lt;/BrowserRouter&gt;
  );
}

Or as a minor optimization, render the Header, Routes, and Footer directly since they are rendered by a pathless route and unconditionally rendered anyway.

function App() {
  return (
    &lt;BrowserRouter&gt;
      &lt;Header /&gt;
      &lt;Routes /&gt;
      &lt;Footer /&gt;
    &lt;/BrowserRouter&gt;
  );
}

答案2

得分: 0

以下是翻译好的部分:

你不能直接将 ...props 作为属性传递到这里,因为在该文件和标头组件之间存在类型不匹配,正如错误中所提到的(history、location、match 和 staticContext)。你需要在标头组件中设置正确的类型(例如,扩展 intrinsicAttributes),如下所示:

interface HeaderProps extends React.HTMLAttributes<HTMLElement> {
  history: History<unknown>;
  location: Location<unknown>;
  match: match<{ [x: string]: string | undefined; }>;
  staticContext?: StaticContext | undefined;
}

function Header(props: HeaderProps) {
  ...
}
英文:

You cannot directly pass ...props as an attribute here, because there is types mismatch between this file and header component, as mentioned in the error (history, location, match, and staticContext). You need to set the proper type in header component (e.g., extend intrinsicAttributes), as:

interface HeaderProps extends React.HTMLAttributes&lt;HTMLElement&gt; {
  history: History&lt;unknown&gt;;
  location: Location&lt;unknown&gt;;
  match: match&lt;{ [x: string]: string | undefined; }&gt;;
  staticContext?: StaticContext | undefined;
}

function Header(props: HeaderProps) {
  ...
}

huangapple
  • 本文由 发表于 2023年6月8日 00:46:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76425501.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定