reactjs 如何使用故事书与trpc

toiithl6  于 2023-02-18  发布在  React
关注(0)|答案(1)|浏览(124)

I have issues creating a story of a component that is using trpc. Storybook is running, but when I open the story I get the following error:
Cannot destructure property 'client' of 'useContext(...)' as it is null. TypeError: Cannot destructure property 'client' of 'useContext(...)' as it is null. at Object.useMutation$1 [as useMutation] (http://localhost:6006/vendors-node_modules_babel_runtime_helpers_esm_defineProperty_js-node_modules_trpc_next_dist_-afeaa2.iframe.bundle.js:6209:17) at http://localhost:6006/vendors-node_modules_babel_runtime_helpers_esm_defineProperty_js-node_modules_trpc_next_dist_-afeaa2.iframe.bundle.js:5884:34 at Object.apply (http://localhost:6006/vendors-node_modules_babel_runtime_helpers_esm_defineProperty_js-node_modules_trpc_next_dist_-afeaa2.iframe.bundle.js:6583:20) at PostSettings (http://localhost:6006/stories-components-feed-postPreview-HeaderPostPreview-stories.iframe.bundle.js:401:92) at renderWithHooks (http://localhost:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-86a76a.iframe.bundle.js:83400:18) at mountIndeterminateComponent (http://localhost:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-86a76a.iframe.bundle.js:87164:13) at beginWork (http://localhost:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-86a76a.iframe.bundle.js:88677:16) at beginWork$1 (http://localhost:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-86a76a.iframe.bundle.js:94516:14) at performUnitOfWork (http://localhost:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-86a76a.iframe.bundle.js:93647:12) at workLoopSync (http://localhost:6006/vendors-node_modules_babel_runtime_helpers_esm_asyncToGenerator_js-node_modules_babel_runtime-86a76a.iframe.bundle.js:93556:5)
@utils/trpc - comes from t3-app

import { httpBatchLink, loggerLink } from '@trpc/client'
import { createTRPCNext } from '@trpc/next'
import { type inferRouterInputs, type inferRouterOutputs } from '@trpc/server'
import superjson from 'superjson'

import { IN_DEV } from '@constants/app'
import { type AppRouter } from '@server/trpc/router/_app'

const getBaseUrl = () => {
  if (typeof window !== 'undefined') return '' // browser should use relative url
  if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}` // SSR should use vercel url
  return `http://localhost:${process.env.PORT ?? 3000}` // dev SSR should use localhost
}

export const trpc = createTRPCNext<AppRouter>({
  config() {
    return {
      transformer: superjson,
      links: [
        loggerLink({
          enabled: (opts) =>
            IN_DEV ||
            (opts.direction === 'down' && opts.result instanceof Error),
        }),
        httpBatchLink({
          url: `${getBaseUrl()}/api/trpc`,
        }),
      ],
    }
  },
  ssr: false,
})

/**
 * Inference helper for inputs
 * @example type HelloInput = RouterInputs['example']['hello']
 **/
export type RouterInputs = inferRouterInputs<AppRouter>
/**
 * Inference helper for outputs
 * @example type HelloOutput = RouterOutputs['example']['hello']
 **/
export type RouterOutputs = inferRouterOutputs<AppRouter>

PostPreview.stories.tsx

import React from 'react'
import type { StoryFn, Meta } from '@storybook/react'
import { trpc } from '@utils/trpc'

const PostPreview = () => {
  const { data, status } = trpc.post.getAll.useQuery()

  if (status === 'loading') {
    return <p>loading</p>
  }
  if (status === 'error' || !data) {
    return <p>error</p>
  }
  return <div>{JSON.stringify(data)}</div>
  
}

export default {
  title: 'Library/PostPreview',
  component: PostPreview,
  argTypes: {},
} as Meta<typeof PostPreview>

const Template: StoryFn<typeof PostPreview> = () => (  <PostPreview />
)

export const Standard = Template.bind({})
kxkpmulp

kxkpmulp1#

在testerez解决方案的帮助下使其工作
@故事/模拟/trpc

/* eslint-disable react/display-name */
import type { PropsWithChildren } from 'react'
import { useState } from 'react'
import { createTRPCReact, httpBatchLink } from '@trpc/react-query'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import superjson from 'superjson'

import type { AppRouter } from '../../server/trpc/router/_app'
import { getBaseUrl } from '@utils/trpc'

export const mockedTrpc = createTRPCReact<AppRouter>()
export const StorybookTrpcProvider = ({ children }: PropsWithChildren) => {
  const [queryClient] = useState(
    new QueryClient({ defaultOptions: { queries: { staleTime: Infinity } } })
  )
  const [trpcClient] = useState(() =>
    mockedTrpc.createClient({
      links: [
        httpBatchLink({
          url: `${getBaseUrl()}/api/trpc`,
        }),
      ],
      transformer: superjson,
    })
  )
  return (
    <mockedTrpc.Provider client={trpcClient} queryClient={queryClient}>
      <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
    </mockedTrpc.Provider>
  )
}

type TrpcContext = ReturnType<(typeof mockedTrpc)['useContext']>

// Hack to be able to access trpcContext
const ActOnTrpcContext = ({
  callback,
  children,
}: PropsWithChildren<{
  callback: (trpcContext: TrpcContext) => void
}>) => {
  const trpcContext = mockedTrpc.useContext()
  callback(trpcContext)
  return <>{children}</>
}

export const withTrpcContext =
  (callback: (context: TrpcContext) => void) => (Story: React.FC) =>
    (
      <ActOnTrpcContext callback={callback}>
        <Story />
      </ActOnTrpcContext>
    )

MyStory.stories.tsx

import type { Meta } from '@storybook/react'

import {
  mockedTrpc,
  StorybookTrpcProvider,
  withTrpcContext,
} from '@stories/mocks/trpc'

// The component to be showcased
const Post = () => {
  const query = mockedTrpc.post.get.useQuery('123')
  return (
    <>
      <div>Status: {query.status}</div>
      <pre>ID: {query.data.id}</pre>
    </>
  )
}

const meta: Meta<typeof Post> = {
  title: 'Post',
  component: Post,
  decorators: [
    (Story) => (
      <StorybookTrpcProvider>
        <Story />
      </StorybookTrpcProvider>
    ),
  ],
}
export default meta

export const UsdWithNoPlugins = {
  render: () => <Post />,
  decorators: [
    withTrpcContext((ctx) => {
      ctx.post.get.setData('123', () => {
        return {
          id: '123',
        }
      })
    }),
  ],
}

相关问题