dai-shi / use-context-selector

React useContextSelector hook in userland
https://www.npmjs.com/package/use-context-selector
MIT License
2.69k stars 61 forks source link

`createContext` breaks tests with `@testing-library/react-native` and `vitest` #123

Closed dcdm3g closed 6 months ago

dcdm3g commented 6 months ago

Hello! I am building a React Native library for personal use and, after migrating from the native Context API to this library, tests for a context, writen with @testing-library/react-native and vitest, broke. This is the error shown:

SyntaxError: Named export 'unstable_batchedUpdates' not found. The requested module 'react-native' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'react-native';
const { unstable_batchedUpdates } = pkg;

 ❯ src/context/cristal-context.spec.tsx:5:25
      3| import { renderHook, act } from '@testing-library/react-native'
      4| import { useContext } from 'use-context-selector'
      5| import { CristalContext, CristalProvider } from '@/context/cristal-context'
                                ^

Any idea what happened? Maybe this is related to #119

package.json

{
  "name": "cristal",
  "version": "1.0.0",
  "description": "Build offline-first react native apps easily",
  "main": "dist/main.js",
  "module": "dist/main.mjs",
  "types": "dist/main.d.ts",
  "scripts": {
    "test": "vitest",
    "test:ui": "vitest --ui",
    "build": "tsup",
    "lint": "eslint ."
  },
  "dependencies": {
    "@react-native-community/netinfo": "^11.3.1",
    "react-native-mmkv": "^2.12.2",
    "scheduler": "^0.23.0",
    "use-context-selector": "^1.4.4",
    "uuid-random": "^1.3.2"
  },
  "peerDependencies": {
    "react": "^18.2.0",
    "react-native": "^0.73.6"
  },
  "devDependencies": {
    "@rocketseat/eslint-config": "^2.2.2",
    "@testing-library/react-native": "^12.4.5",
    "@types/react": "^18.2.79",
    "@vitejs/plugin-react": "^4.2.1",
    "@vitest/ui": "^1.5.0",
    "eslint": "^8.57.0",
    "eslint-plugin-testing-library": "^6.2.2",
    "tsup": "^8.0.2",
    "typescript": "^5.4.5",
    "vitest": "^1.5.0",
    "vitest-react-native": "^0.1.5"
  }
}

vitest.config.mts

import { defineConfig } from 'vitest/config'
import rn from 'vitest-react-native'
import react from '@vitejs/plugin-react'
import { resolve } from 'node:path'

export default defineConfig({
  plugins: [rn(), react()],
  test: {
    server: {
      deps: {
        inline: ['react-native'],
      },
    },
    setupFiles: ['vitest.setup.ts'],
  },
  resolve: {
    alias: [{ find: '@', replacement: resolve(__dirname, 'src') }],
  },
})

@/context/cristal-context.tsx (where the test pointed out the error)

import type { Resources } from '@/common/types/resources'
import type { Dispatch, SetStateAction, ReactNode } from 'react'
import { createContext } from 'use-context-selector'
import { useState, useEffect } from 'react'
import { db } from '@/db/connection'

type CristalContextValue = {
  resources: Resources
  setResources: Dispatch<SetStateAction<Resources>>
} | null

export const CristalContext = createContext<CristalContextValue>(null)

interface CristalProviderProps {
  children: ReactNode
}

export function CristalProvider({ children }: CristalProviderProps) {
  const [resources, setResources] = useState<Resources>({})

  useEffect(() => {
    const data = db.get<Resources | undefined>('cristal-resources')
    if (data) setResources(data)
  }, [])

  return (
    <CristalContext.Provider value={{ resources, setResources }}>
      {children}
    </CristalContext.Provider>
  )
}
dai-shi commented 6 months ago

Thanks for reporting. This is because this library is implemented under "bundler esm" assumption, but vite requires "full esm". Can you try if https://github.com/vite-plugin/vite-plugin-commonjs works it around?

dcdm3g commented 6 months ago

Thanks for the answer! Sorry for my delay in responding!

I can't test this right now, but I promise in less than 4 hours I will do it.

dcdm3g commented 6 months ago

Problem solved! 🎉

I tried to solve it with the vite-plugin-commonjs package, but it didn't work.

So I started researching the subject more and discovered that I could just add 'use-context-selector' to vitest's server.deps.inline configuration to solve the problem:

{
  server: {
    deps: {
      inline: ['react-native', 'use-context-selector'] // Just added use-context-selector here
    },
  },
}

Thank you very much for the support!