实用程序组件内部的钩子

Hooks inside of utility components

我正在努力学习 Ben Awad 的 lireddit 教程。

他展示了如何制作一个实用程序文件来显示应用程序中的任何身份验证错误。

我认为 next/router 自从他制作了视频后情况可能发生了变化。我正在努力寻找一种方法来使用路由器以这种方式在组件内部的钩子内部重定向。

我不能将 useRouter 挂钩放在 exchangeError 中 - 我认为原因是当我想在其他组件中使用 exchangeError 时,挂钩不再位于顶层,但我可以'弄清楚如何在没有它的情况下使 exchangeError 工作,或者适应 next/router.

的更新版本

我最好的尝试(我知道我不能这样使用它)如下:

import { dedupExchange, fetchExchange, createClient, Exchange } from "urql";
import {  pipe, tap } from 'wonka'
import { useRouter } from 'next/router'


const errorExchange: Exchange = ({ forward }) => (ops$) => {
    // where can i put this
    // const Router = useRouter();

    return pipe(
      forward(ops$),
      tap(({ error }) => {
        if (error?.message.includes("not authenticated")) {
        //   Router.replace("/auth/login");
        }
      })
    );
  };

Ben 在 lireddit 发布的版本是:

const errorExchange: Exchange = ({ forward }) => (ops$) => {
  return pipe(
    forward(ops$),
    tap(({ error }) => {
      if (error?.message.includes("not authenticated")) {
        Router.replace("/login");
      }
    })
  );
};

整个实用程序文件如下:

import { dedupExchange, fetchExchange, createClient, Exchange } from "urql";
import { cacheExchange } from '@urql/exchange-graphcache'
import { MeDocument, LoginMutation, RegisterMutation, MeQuery, LogoutMutation } from "../generated/graphql"
import { betterUpdateQuery } from '../utils/betterUpdateQuery'
import {  pipe, tap } from 'wonka'
import { useRouter } from 'next/router'


const errorExchange: Exchange = ({ forward }) => (ops$) => {
    // where can i put this
    // const Router = useRouter();

    return pipe(
      forward(ops$),
      tap(({ error }) => {
        if (error?.message.includes("not authenticated")) {
        //   Router.replace("/auth/login");
        }
      })
    );
  };


export const createUrqlClient = (ssrExchange: any) => (
    {
        url: 'http://localhost:4000/graphql',
        fetchOptions: {
            credentials: 'include' as const,
        },
        exchanges: [
            dedupExchange,
            cacheExchange({
            updates: {
                Mutation: {
                logout: (_result, args, cache, info) => {
                    betterUpdateQuery<LogoutMutation, MeQuery>(
                    cache,
                    {query: MeDocument},
                    _result,
                    (result, query) => ({me: null})
                    )
                },
                login: (_result, args, cache, info) => {
                    betterUpdateQuery<LoginMutation, MeQuery>(
                    cache,
                    { query: MeDocument },
                    _result,
                    (result, query) => {
                        if (result.login.errors) {
                        return query;
                        } else {
                        return {
                            me: result.login.user,
                        };
                        }
                    }
                    );
                },
                register: (_result, args, cache, info) => {
                    betterUpdateQuery<RegisterMutation, MeQuery>(
                    cache,
                    { query: MeDocument },
                    _result,
                    (result, query) => {
                        if (result.register.errors) {
                        return query;
                        } else {
                        return {
                            me: result.register.user,
                        };
                        }
                    }
                    );
                },
                },
            },
            }),
            errorExchange,
            ssrExchange,
            fetchExchange,
        ],
    } 
)

然后,在创建 post 表单中,此实用程序的使用如下:

import { withUrqlClient } from "next-urql";
import { useRouter } from "next/router";
import React from "react";
import { useCreatePostMutation } from "../generated/graphql";
import { createUrqlClient } from "../utils/createUrqlClient";
import { useIsAuth } from "../utils/useIsAuth";

const CreatePost: React.FC<{}> = ({}) => {
  // const router = useRouter();
  // useIsAuth();
  const [, createPost] = useCreatePostMutation();
  return (
    <Layout variant="small">
      <Formik
        initialValues={{ title: "", text: "" }}
        onSubmit={async (values) => {
          const { error } = await createPost({ input: values });
          if (!error) {
            router.push("/");
          }
        }}
      >

export default withUrqlClient(createUrqlClient)(CreatePost);

所以我花了一些时间研究 source code。结论:

import { dedupExchange, fetchExchange, createClient, Exchange } from "urql";
import { pipe, tap } from 'wonka'
import Router from 'next/router'

const errorExchange: Exchange = ({ forward }) => (ops$) => {

    return pipe(
      forward(ops$),
      tap(({ error }) => {
        if (error?.message.includes("not authenticated")) {
          Router.router.replace("/auth/login");  // <-- `Router.router` is the way
        }
      })
    );
  };

我不知道他们为什么决定以这种方式公开 API。 Router.router 我觉得很奇怪,但这就是解决方案。


注意为什么 useRouter 钩子不是一个选项。

Hook 必须在 React 组件实例中调用。但是 errorExchange 意味着被 createUrqlClient 调用,而 createUrqlClient 又被 withUrqlClient(createUrqlClient)(CreatePost) 调用。

withUrlClient 是一个装饰器,在 React 的术语中是 HOC,而 so I checked 它调用 errorExchange 的时机恰好不适合 hook 函数介入,因此死胡同。