简体   繁体   中英

Why is export from index.tsx file undefined? (React + Typescript)

I have a simple Context Provider that I want to export as a npm package.

I created my npm package using create-react-library .

I have created an index.tsx file that will be accessible when importing this npm package from another project; I am importing the Context Provider to this index.tsx, then exporting it.

When I try and run a test with the ContextProvider imported from index.tsx, the imported object is undefined (even though I can navigate to it in VS Code). However, when I import it directly from the source, it works fine. I will need to have it working from the index.tsx file in order to access it through the npm package.

Can anyone explain to me what I'm missing here?

src/index.tsx

import ErrorHandler from './contexts/ErrorHandlerContextProvider'

export { ErrorHandler }

// export { default as ErrorHandler } from './contexts/ErrorHandlerContextProvider' //<-- no luck

src/index.test.tsx

// import ErrorHandler from './contexts/ErrorHandlerContextProvider' // <-- this import works
import { ErrorHandler } from '.'

describe('ErrorHandlerContextProvider', () => {
  it('is truthy', () => {
    expect(ErrorHandler.ErrorHandlerContextProvider).toBeTruthy()
  })
})

src/contexts/ErrorHandlerContextProvider.tsx

import React, { createContext, FC, useContext } from 'react'
import PropTypes from 'prop-types'

type errorHandlerContextType = {
  handleError: (error: Error, info: string) => Promise<void>
}

const ErrorHandlerContext = createContext<errorHandlerContextType | null>(null)

const useErrorHandlerContextProvider = () => {
  return useContext(ErrorHandlerContext)
}

const ErrorHandlerContextProvider: FC = ({ children }) => {

  const handleError = (error: Error, info: string): Promise<void> => {
    console.log('error', error)
    console.log('info', info)

    return Promise.reject(error)
  }

  return (
    <ErrorHandlerContext.Provider value={{ handleError }}>
      {children}
    </ErrorHandlerContext.Provider>
  )
}

ErrorHandlerContextProvider.propTypes = {
  children: PropTypes.node.isRequired
}

export default { ErrorHandlerContextProvider, useErrorHandlerContextProvider }

/tsconfig.json

{
  "compilerOptions": {
    "outDir": "dist",
    "module": "esnext",
    "lib": ["dom", "esnext"],
    "moduleResolution": "node",
    "jsx": "react",
    "sourceMap": true,
    "declaration": true,
    "esModuleInterop": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "allowSyntheticDefaultImports": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist", "example"]
}

npm run test

FAIL src/index.test.tsx ● ErrorHandlerContextProvider › is truthy

TypeError: Cannot read property 'ErrorHandlerContextProvider' of undefined

  4 | describe('ErrorHandlerContextProvider', () => {
  5 |   it('is truthy', () => {
> 6 |     expect(ErrorHandler.ErrorHandlerContextProvider).toBeTruthy()
    |                         ^
  7 |   })
  8 | })
  9 |

  at Object.<anonymous> (src/index.test.tsx:6:25)

This seems really suspicious:

// src/index.test.tsx

// works
// import ErrorHandler from './contexts/ErrorHandlerContextProvider'

// does not work
import { ErrorHandler } from '.'

Since the disabled import works, we know that the impl file's default export is working. But your index has no default export (which seems like bad manners for npm, but you do you). This tells me that either the test file is importing the impl wrongly, or the barrel file is. (I think it is unlikely that the index is exporting the impl wrongly.)

All your other imports, exports, and declarations seem correct on casual inspection.

So here is a grab-bag of ideas:

  • Try using the actual filename instead of just from '.'
  • The barrel has no React in it, so try renaming index.tsx to index.ts
  • Confirm that your test setup uses the same settings as the app code when it comes to default file extensions & default filenames

If none of those does it, try using your basic error-trapping skills to pinpoint the first broken link in the chain.

  1. Add console.log("ErrorHandler", ErrorHandler) to your index after the import, then run the tests again and look for that log statement to confirm that the imported thing looks like you'd expect.
  2. Try the same thing inside the test file, to examine what is imported from the barrel file.

The goal is to figure out at which step of the import chain things go awry.


Also: ES6 destructuring is not the same thing as ES6 named imports/exports, and you can't mix-and-match. So, I believe this code should not work :

// file1.js
export default { property: 'value' }


// file2.js
import { property } from './file1' // should fail

The reason is that file1 does not have named exports, it has only a default export, and the import statement in file2 is not permitted to destructure that object while importing it. The code in file2 should be read as: "import named export 'property' from file1" -- which will not work because file1 has no named exports.

In all honesty, I don't see a violation of this here, but it's something to keep in mind.


One final thought: don't organize your tests like this.

  1. The test file for ErrorHandlerContextProvider should live in the same folder as the impl of the provider (or in a nested __tests folder, according to your preference). The proper job of that test suite is to test the actual impl of the provider.
  2. If you even bother with an index.test.tsx , the proper job of that suite is just to prove that the public API of your npm module has the right shape. It has no business testing the actual functionality.

I would make this change even if it's not necessary to fix the problem. But, I'd hold off on making the change until after you have figured out precisely what the underlying problem is (and fixed it).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM