Thành's Blog

Tự làm UI Library với React và Typescript

April 24, 2019 • 3 mins

Câu chuyện tái sử dụng code lúc nào cũng đau não. Mình cố gắng viết ngon lành nhất nhưng mỗi lần muốn dùng một component nào lại đau não xem nó ở project nào. Lục một lúc không thấy lại phải loay hoay viết lại. Thế nên, ngon nhất là cất nó trên npm. Sau cần dùng là chỉ việc quất yarn add ui-library về là ngon luôn.

Hôm nay, mình không đau ốm gì như hôm viết bài tự làm cli nhưng rảnh háng nên lại viết bài này. Quẩy thôi anh em 🤣

Todo

  • Init project
  • Sử dụng Storybook để code component
  • Sử dụng Storybook để làm documentation
  • Viết test
  • Setup CI ?!?!
  • Publish library lên npm
  • Publish documentation lên github pages hoặc firebase hosting

Init project

Lại lệnh lủng các thứ

mkdir react-library && cd $_yarn init -y

Có thể bạn biết rồi!

$_ sẽ đại diện cho argument cuối cùng của lệnh phía trước nó và sử dụng cho lệnh tiếp theo, giúp bạn đỡ cảm thấy mệt mỏi khi phải gõ quá nhiều 😎

$ echo a b && echo $_
# Output
# a b
# b

Cài devDependencies

yarn add -D typescript react react-dom @storybook/react babel-loader @babel/core shx
yarn add -D @types/node @types/react @types/react-dom @types/storybook__react

Config typescript và storybook

Tạo file tsconfig.json

{
  "compilerOptions": {
    "outDir": "dist",
    "module": "es6",    "target": "es5",
    "lib": ["es5", "es6", "es7", "es2017", "dom"],
    "jsx": "react",
    "moduleResolution": "node",
    "rootDirs": ["src", "stories"],
    "baseUrl": "src",
    "strict": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noUnusedLocals": true,
    "declaration": true,
    "allowSyntheticDefaultImports": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Nếu để module: "commonjs", library sẽ không dùng được với create-react-app. Vì tsc và build script của cra không chơi với nhau. Không biết do anh nào nhưng để như trên thì các anh ý mới chơi với nhau. Các anh em dùng webpack thì tự config cho phù hợp.

Sửa file package.json

{
  // NPM's package name
  // Sau này quất về bằng yarn add @thanhtc/ui chẳng hạn
  "name": "@thanhtc/ui",
  // ...
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "clean": "shx rm -rf dist",
    "start": "start-storybook",
    "build:ui": "yarn clean && tsc",
    "build:docs": "build-storybook -o docs",
  }
}

Tạo file .storybook/config.ts

import { addParameters, configure } from '@storybook/react'

const req = require.context('../stories', true, /\.stories\.tsx$/)
function loadStories() {
  req.keys().forEach(req)
}

configure(loadStories, module)

Storybook sẽ tự load tất cả các file story kết thúc bằng .stories.tsx trong folder stories cho anh em.

Tạo file .storybook/webpack.config.js

module.exports = ({ config, mode }) => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    loader: require.resolve('babel-loader'),
    options: {
      presets: [['react-app', { flow: false, typescript: true }]],
    },
  });
  config.resolve.extensions.push('.ts', '.tsx')
  return config;
}

Dev nào

Component đầu tiên

Tạo file src/Component.tsx

import React from 'react'

type Props = {}

export const Component: React.FC<Props> = () => (<div>Hello anh em!</div>)

Tạo file stories/Component.stories.tsx

import React from 'react'
import { storiesOf } from '@storybook/react'
import { Component } from '../src/Component'

+ storiesOf('Component', module).add('Story 1', () => <Component />)

Chạy yarn start anh em sẽ thấy

Storybook

Storybook cũng hot reload như anh em dev trong app của mình vậy.

Build & Publish

Dài quá rồi! Mai viết tiếp nhé 🤣

2018 © Thành
RSS