简体   繁体   中英

How to bundle prop types in a reusable React component library

I'm trying to make a simple React components library for fun using Typescript and Storybook, and my first silly Button component seems to work. I have another CRA demo app where I am trying the library as a final user.

I am stuck with a thing though: I can't make the prop-types of my Button component to work in the demo application. It just doesn't print the warnings.

Some additional details:

Files and folder of the library are currently organized as follows:

src/
  index.ts
  controls/
    index.ts
    Button.tsx
babelconfig.js
tsconfig.ts
webpack.config.js

(+ other configuration files and folder for jest and storybook)

I have an src/index.ts file which is the entry point of my library and it just exports everything that comes from the controls folder.

// src/index.ts
export * from './controls'

In my src/controls folders there's an index.ts file which exports everything from the Button component.

// src/controls/index.ts
export * from './Button'

The Button component, at the moment, is as simple as:

import React from 'react'
import t, { InferProps } from 'prop-types'

const propTypes = {
  text: t.string.isRequired
}

const Button = ({ text }: InferProps<typeof propTypes>) => (
  <button>
    { text }
  </button>
)

Button.propTypes = propTypes

export { Button }

Everything is bundled with webpack 4.41 with this configuration file:

// webpack.config.js

const path = require('path')

module.exports = {
  mode: 'production',
  entry: './src/index.ts',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.js',
    library: 'sads-ui',
    libraryTarget: 'umd'
  },
  externals: [
    'react'
  ],
  resolve: {
    extensions: [ '.ts', '.tsx', '.js' ]
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  }
}

My babel configuration is this one:

// babel.config.js

module.exports = {
  presets: [
    '@babel/preset-env',
    '@babel/preset-react',
    '@babel/preset-typescript'
  ],
  env: {
    test: {
      plugins: [ 'require-context-hook' ]
    }
  }
}

I'm not sure if it may matter, but this is my typescript 3.7 configuration file:

// tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "es6",
    "jsx": "react",
    "declaration": true,
    "outDir": "./dist",
    "downlevelIteration": true,
    "strict": true,
    "noImplicitAny": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "baseUrl": ".",
    "typeRoots": [ "node_modules/@types" ],
    "esModuleInterop": true
  }
}

The main properties of my package.json file are these one:

// other properties

"main": "dist/index.js",
"files": [
  "dist/*",
  "!dist/**/*.stories.d.ts"
],
"scripts": {
  "prebuild": "rimraf ./dist ./*.tgz",
  "build": "webpack",
  "prepack": "npm run build",
  "test": "jest",
  "storybook": "start-storybook"
},

// other properties

Expectations

In my demo app, created with npx create-react-app sadsui-demo , I installed the package in my package.json with a dependency like this:

// package.json

"sads-ui": "file:../sads-ui/sads-ui-0.1.0.tgz"

I use the Button in my App.js as follow:

import React from 'react'

import { Button } from 'sads-ui'

const App = () => (
  <div className="App">
    <Button />
  </div>
)

export default App

I would expect to get a warning the tells me that the text prop of Button is required, but I get nothing. An empty button is correctly shown though.

My reasoning

I'm somehow in the dark but I have something in mind, not sure if it makes any sense, also because it doesn't seem to work either.

I think that building the library in webpack production mode may strip off prop-types from the library bundle thus I don't have them available when I use the components in final applications.

I tried to bundle in development mode, I see in the dist/index.js the prop-types code but they aren't working no matter what... :(

Your webpack is compiling (everything matching *.tsx) to javascript when building the library, and when you import it into your create-react-app it is just javascript (you should go into your /node_modules/ in your create-react-app and actually see for yourself what your library looks like)

In your library, you can export separate index.d.ts (or Button.d.ts or whatever) files for your components that are not compiled to JS that consumers can import (Always good to look at other projects and see how they do it. Here is material ui's index.d.ts , you can probably be a lot cleaner than that by having a Button.d.ts that is imported into the index.d.ts and then exported as one big library typescript module

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