preact にも hooks が生えそうなのでやっていく

軽量で react ライクなライブラリである preact に hooks が入りそうなのをたまたま見つけたので久しぶりにやっていく

## やっていくライン

 1. webpack-dev-server で hot-reload 開発
 2. webpack を介した bundle
   1. preact gzip で 3kb 説検証
 3. webpack 外で静的ホスト

## setup

もろもろ準備する

### package.json
```json:package.json
  "scripts": {
    "dev": "webpack-dev-server --config ./webpack.config.js --mode development",
    "build": "npm run clean && npm run build:webpack && cp ./dist/bundle.js ./public/",
    "build:webpack": "webpack --config ./webpack.config.js",
    "clean": "rm -rf ./dist/ ./public/bundle.*",
    "start": "serve ./public",
    "init": "npm i && mkdir src public && touch src/index.js public/index.html babel.config.js webpack.config.js",
    "gzip": "gzip -k9rn ./dist"
  },
  "dependencies": {
    "preact": "10.0.0-beta.2"
  },
  "devDependencies": {
    "@babel/cli": "7.4.4",
    "@babel/core": "7.4.5",
    "@babel/plugin-transform-react-jsx": "7.3.0",
    "@babel/preset-env": "7.4.5",
    "babel-loader": "8.0.6",
    "serve": "11.0.1",
    "webpack": "4.33.0",
    "webpack-cli": "3.3.3",
    "webpack-dev-server": "3.7.1"
  },
```

- webpack, babel
    - 近代では必須
- preact
  -  やっていく module
- [serve](https://github.com/zeit/serve)
    - 静的ホスト簡易サーバ


初期化コマンドで諸々用意する

```bash:
npm run init
```


### babel
```javascript:babel.config.js
const presets = [
  [
    '@babel/env',
    {
      targets: {
        chrome: '75',
      },
    },
  ],
];
const plugins = [['@babel/plugin-transform-react-jsx', { pragma: 'h' }]];
module.exports = { presets, plugins };
```

とりあえず chrome で動けばいいので targets は適当に指定しとく
[公式](https://preactjs.com/guide/getting-started)に載ってる通りに JSX をコンパイルできるようにする plugin だけ入れとく

### webpack
```javascript:webpack.config.js
module.exports = {
  entry: ['./src/index.js'],
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ['babel-loader'],
      },
    ],
  },
  resolve: {
    extensions: ['*', '.js', '.jsx'],
  },
  output: {
    path: __dirname + '/dist',
    publicPath: '/',
    filename: 'bundle.js',
  },
  devServer: {
    contentBase: './public',
  },
};
```

まあ packer の皆さんなら特に難しいところもないと思うので割愛

## development

まずはハロワを目指す


```html:public/index.html
<!DOCTYPE html>
<html>
  <head>
    <title>preact starter</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <script src="/bundle.js" async></script>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
```

```jsx:src/index.js
import { h, render } from 'preact';

const App = ({}, {}) => <div>Hello Preaaaaaaaaact</div>;

render(<App />, document.getElementById('app'));
```

```bash:dev-server
npm run dev
```

多分これでいけてると思う
あとは好きに修正すればokということでやっていきライン1はクリア

ここで build も試す

```bash
npm run build && npm run gzip && ls -lah dist/
# ... 3.5K ... bundle.js.gz
```

import cost と同じ数値なので成功ということでやっていきライン2もクリア
続けて3のクリアも目指す

```bash
npm start
```

全クリできたと思う
静的ホスティングはnowでもねっとりファイでもなんでもいいと思う

### hooks

ついでに preact の hooks をいくつか書いて終わりたいと思う

```jsx:src/index.js
import { h, render } from 'preact';
import { useState, useMemo, useLayoutEffect, useCallback } from 'preact/hooks';

const Clock = () => {
  const [time, setTime] = useState(0);

  const double = useMemo(() => {
    return time * 2;
  }, [time]);

  const increment = useCallback(() => {
    setTime(time + 1);
  }, [time]);
  const decrement = useCallback(() => {
    setTime(time - 1);
  }, [time]);

  useLayoutEffect(() => {
    time !== 0 && time % 3 === 0 && setTime(time + 1);
  }, [time]);

  return (
    <div>
      {time}
      {double}
      <button onclick={increment}>++</button>
      <button onclick={decrement}>--</button>
    </div>
  );
};

const App = () => (
  <div>
    Hello Preaaaaaaaaact
    <Clock />
  </div>
);

render(<App />, document.getElementById('app'));
```

流石に書き味は同じだった
ちなみに preact/hooks の bundle size は gzip で 3.5kb くらい