解读React Router V6官方文档(三)Hook

1,115 阅读4分钟

useHref

useHref钩子返回一个URL,该URL可以用来链接到指定的位置,即使是在React路由器之外。

declare function useHref(to: To): string;

Tip:

可以看看react-router-dom中的Link组件,如何用useHref决定自己的href值的

const Link = React.forwardRef(
    ({
        onClick,
        replace = false,
        state,
        target,
        to,
        ...rest
    }, ref) => {
        let href = useHref(to);
        let handleClick = useLinkClickHandler(to, {
            replace,
            state,
            target,
        })
        
        return (
            <FancyPantsLink 
                {...rest}
                href={href}
                onClick={(event) => {
                    onClick?.(event);
                    if (!event.defaultPrevented) {
                        handleClick(event);
                    }
                }}
            />
        )
    }
)

useInRouterContext

declare function useInRouterContext(): boolean;

如果组件是渲染在Router上下文中,useInRouterContext hook将会返回true,否则返回false。

这个常用于第三方拓展库判断自己是否处于React Router环境中。

useLinkClickHandler

declare function useLinkClickHandler<
  E extends Element = HTMLAnchorElement
>(
  to: To,
  options?: {
    target?: React.HTMLAttributeAnchorTarget;
    replace?: boolean;
    state?: any;
  }
): (event: React.MouseEvent<E, MouseEvent>) => void;

useLinkClickHandler hook在建立一个自定义Link的时候,返回了一个点击事件处理器用于导航。

import {
  useHref,
  useLinkClickHandler,
} from "react-router-dom";

const StyledLink = styled("a", { color: "fuchsia" });

const Link = React.forwardRef(
  (
    {
      onClick,
      replace = false,
      state,
      target,
      to,
      ...rest
    },
    ref
  ) => {
    let href = useHref(to);
    let handleClick = useLinkClickHandler(to, {
      replace,
      state,
      target,
    });

    return (
      <StyledLink
        {...rest}
        href={href}
        onClick={(event) => {
          onClick?.(event);
          if (!event.defaultPrevented) {
            handleClick(event);
          }
        }}
        ref={ref}
        target={target}
      />
    );
  }
);

useLinkPressHndler

declare function useLinkPressHandler(
  to: To,
  options?: {
    replace?: boolean;
    state?: any;
  }
): (event: GestureResponderEvent) => void;

常用于React-Native,对应着useLinkClickHandler,为自定义Link返回一个按压事件处理器

import { TouchableHighlight } from "react-native";
import { useLinkPressHandler } from "react-router-native";

function Link({
  onPress,
  replace = false,
  state,
  to,
  ...rest
}) {
  let handlePress = useLinkPressHandler(to, {
    replace,
    state,
  });

  return (
    <TouchableHighlight
      {...rest}
      onPress={(event) => {
        onPress?.(event);
        if (!event.defaultPrevented) {
          handlePress(event);
        }
      }}
    />
  );
}

useLoction

declare function useLocation(): Location;

interface Location extends Path {
  state: unknown;
  key: Key;
}

hook返回当前location对象,可用于当location改变时,执行一些effect

import * as React from 'react';
import { useLocation } from 'react-router-dom';

function App() {
  let location = useLocation();

  React.useEffect(() => {
    ga('send', 'pageview');
  }, [location]);

  return (
    // ...
  );
}

useMatch

declare function useMatch<ParamKey extends string = string>(
  pattern: PathPattern | string
): PathMatch<ParamKey> | null;

给定相对路径,返回所匹配路由的相关数据

export interface PathMatch<ParamKey extends string = string> {
 // URL中的动态参数,如?id=123&&name=kk,id和kk就是两个动态参数
  params: Params<ParamKey>;
  pathname: string;
  // 子路由之前匹配的URL部分
  pathnameBase: string;
  pattern: PathPattern;
}

useNavigate

declare function useNavigate(): NavigateFunction;

interface NavigateFunction {
  (
    to: To,
    options?: { replace?: boolean; state?: any }
  ): void;
  (delta: number): void;
}

useNavigate hook返回了一个函数,可以让你编程式导航。如果用了replace:true,在history栈中,它将会代替当前的入口,而不是添加一个新的进去。

import { useNavigate } from 'react-router-dom';

function SignupForm() {
    let navigate = useNavigate();
    
    async function handleSubmit(event) {
        event.preventDefault();
        awat sunmitForm(event.target);
        navigate('../success'. { replace: true })
    }
    
    return <form onSubmit={handleSubmit}>{...}</form>
}

useNavigateType

declare function useNavigationType(): NavigationType;

type NavigationType = "POP" | "PUSH" | "REPLACE";

返回导航的类型,表示user是如何来到当前页面的;是在history stack中通过pop,push还是replace

useOutlet

declare function useOutlet(): React.ReactElement | null;

为子路由返回React元素。内部也是通过来渲染子路由的

useOutletContext

declare function useOutletContext<
  Context = unknown
>(): Context;

想和其他子路由共享父路由管理状态或者值,可以创建context provider,它已经内置于Outlet中了

function Parent() {
    const [count, setCount] = React.useState(0);
    return <Outlet context={[count, setCount]} />
}
import { useOutletContext } from 'react-router-dom'

function Child() {
    const [count, setCount] = useOutletContext();
    const increment = () => setCount(c => c + 1);
    return <button onClick={increment}>{count}</button>
}

如果使用的是TypeScript,官方建议父组件提供一个自定义hook来获取context值。

// dashboard.js
import React from 'react'
import type { User } from "./types";
import { Outlet, useOutletContext } from "react-router-dom";

export default function Dashboard() {
  const [user, setUser] = React.useState<User | null>(null);

  return (
    <div>
      <h1>Dashboard</h1>
      <Outlet context={{ user }} />
    </div>
  );
}

// 自定义hook
export function useUser() {
  return useOutletContext<ContextType>();
}
import { useUser } from "../dashboard";

export default function DashboardMessages() {
  const { user } = useUser();
  return (
    <div>
      <h2>Messages</h2>
      <p>Hello, {user.name}!</p>
    </div>
  );
}

useParams

declare function useParams<
  K extends string = string
>(): Readonly<Params<K>>;

useParams hook返回了当前URL动态参数的键值对

import React from 'react';
import { Routes, Route, useParams } from 'react-router-dom';

function ProfildPage() {
    let { userId } = useParams();
}

function App() {
  return (
    <Routes>
      <Route path="users">
        <Route path=":userId" element={<ProfilePage />} />
        <Route path="me" element={...} />
      </Route>
    </Routes>
  );
}

useResolvedPath

declare function useResolvedPath(to: To): Path;

根据给定的to值解析当前location的pathname

这有助于从相对路径中建立links。比如,在NavLink源码内部调用了useResolvedPath解析了页面对应的完整pathname

export const NavLink = React.forwardRef<HTMLAnchorElement, NavLinkProps>(
  function NavLinkWithRef(
    {
      "aria-current": ariaCurrentProp = "page",
      caseSensitive = false,
      className: classNameProp = "",
      end = false,
      style: styleProp,
      to,
      children,
      ...rest
    },
    ref
  ) {
    let location = useLocation();
    let path = useResolvedPath(to); // useResolvedPath获得完整的pathname

    let locationPathname = location.pathname;
    let toPathname = path.pathname;
    if (!caseSensitive) {
      locationPathname = locationPathname.toLowerCase();
      toPathname = toPathname.toLowerCase();
    }

    let isActive =
      locationPathname === toPathname ||
      (!end &&
        locationPathname.startsWith(toPathname) &&
        locationPathname.charAt(toPathname.length) === "/");

    let ariaCurrent = isActive ? ariaCurrentProp : undefined;

    let className: string | undefined;
    if (typeof classNameProp === "function") {
      className = classNameProp({ isActive });
    } else {
      className = [classNameProp, isActive ? "active" : null]
        .filter(Boolean)
        .join(" ");
    }

    let style =
      typeof styleProp === "function" ? styleProp({ isActive }) : styleProp;

    return (
      <Link
        {...rest}
        aria-current={ariaCurrent}
        className={className}
        ref={ref}
        style={style}
        to={to}
      >
        {typeof children === "function" ? children({ isActive }) : children}
      </Link>
    );
  }
);

useRoutes

useRoutes hook等价于Routes,但它使用js定义路由,而不是jsx。这些对象有着和Route元素一样的属性。useRoutes返回值可以是一个合法的React element,用来渲染route tree或者null

import * as React from "react";
import { useRoutes } from "react-router-dom";

fucntion Apoo() {
    let element = useRoutes([
        path: '/',
        element: <Dashboard />,
        chlidren: [
            {
                path: 'message',
                element: <DashboardMessage />
            },
            {
                path: 'tasks',
                element: <DashboardTasks />
            }
        ]
    ])
}

useSearchParams

用于读取和修改URL中的query字符串,和useState一样,useSearchParams返回了带有两个值的数组:当前的location的search参数,更新参数的函数

let [searchParams, setSearchParams] = useSearchParams();




import * as React from "react";
import { useSearchParams } from "react-router-dom";

function App() {
    let [searchParams, setSearchParams] = useSearchParams();
    
    function handleSubmit(event) {
        event.preventDefault();
        
        let params = serializeFormuery(event.target);
        setSearchParams(params);
    }
    
     return (
        <div>
          <form onSubmit={handleSubmit}>{/* ... */}</form>
        </div>
      );
      
}

setSearchParams函数的工作原理类似于导航,但只用于URL的搜索部分。setSearchParams的第二个参数也是和navigate一样。