From 551ef58d4a22fd9004dd5184106d5f46db59f870 Mon Sep 17 00:00:00 2001 From: ddhp Date: Fri, 23 Nov 2018 18:15:16 +0900 Subject: [PATCH 1/9] setup rhl in v4 way --- src/entries/template.js | 24 +++++++++++++++++++----- webpack.browser.babel.js | 2 +- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/entries/template.js b/src/entries/template.js index 27ecc27..ca1a380 100644 --- a/src/entries/template.js +++ b/src/entries/template.js @@ -2,8 +2,15 @@ import React from 'react'; import { hydrate } from 'react-dom'; import { Provider } from 'react-redux'; import { BrowserRouter as Router } from 'react-router-dom'; +import { hot, setConfig } from 'react-hot-loader'; import configureStore from '../configureStore'; +setConfig({ + ignoreSFC: true, // RHL will be __complitely__ disabled for SFC + // pureSFC: true, + // pureRender: true, // RHL will not change render method +}); + /** * you can REUSE this template for different entries * @@ -25,12 +32,19 @@ export default function mount(Routes, rootReducer) { // Create Redux store with initial state const store = configureStore(reduxState, rootReducer); + function App() { + return ( + + + + + + ); + } + const HotApp = hot(module)(App); + hydrate( - - - - - , + , document.getElementById('app-mount-point'), ); } diff --git a/webpack.browser.babel.js b/webpack.browser.babel.js index d775076..990dd98 100644 --- a/webpack.browser.babel.js +++ b/webpack.browser.babel.js @@ -68,7 +68,7 @@ export default function browserConfig(env) { const devEntry = {}; Object.keys(entry).forEach((key) => { devEntry[key] = []; - devEntry[key].push('react-hot-loader/patch', entry[key], hotMiddlewareScript); + devEntry[key].push(entry[key], hotMiddlewareScript); }); config.entry = devEntry; From d52160007556a440f0db469a8187a6dc34409d37 Mon Sep 17 00:00:00 2001 From: ddhp Date: Fri, 23 Nov 2018 18:36:09 +0900 Subject: [PATCH 2/9] upgrade to react v16.7-alpha and a component with hook --- .eslintrc.yml | 3 ++ README.md | 1 + package.json | 9 ++-- src/containers/Home/FormPostTotalCount.js | 20 +++++++++ src/containers/Home/index.js | 2 + src/containers/Home/style.scss | 17 +++++++ src/hooks/useRedux.js | 11 +++++ yarn.lock | 55 +++++++++++++++-------- 8 files changed, 95 insertions(+), 23 deletions(-) create mode 100644 src/containers/Home/FormPostTotalCount.js create mode 100644 src/hooks/useRedux.js diff --git a/.eslintrc.yml b/.eslintrc.yml index 3baeae1..94aadd5 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -4,6 +4,8 @@ env: extends: - 'airbnb' parser: 'babel-eslint' +plugins: + - 'react-hooks' rules: react/jsx-filename-extension: - error @@ -24,3 +26,4 @@ rules: - 'Link' specialLink: - 'to' + react-hooks/rules-of-hooks: 'error' diff --git a/README.md b/README.md index 8750142..6ae18a8 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ This boilerplate would help you build a react/redux/react-router isomorphic/univ - manage your style in [CSS Modules](https://github.com/css-modules/css-modules) way. - babel v7 - optimize bundle size by implementing [tree shaking](https://webpack.js.org/guides/tree-shaking/) +- react hooks!(I hate OO components) ## Concept ### Getting Started diff --git a/package.json b/package.json index 8d83ec5..1a25adb 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "eslint-plugin-import": "^2.14.0", "eslint-plugin-jsx-a11y": "^6.1.1", "eslint-plugin-react": "^7.11.1", + "eslint-plugin-react-hooks": "^0.0.0", "file-loader": "^2.0.0", "ignore-styles": "^5.0.1", "mini-css-extract-plugin": "^0.4.4", @@ -84,11 +85,11 @@ "lodash": "^4.17.4", "normalizr": "^3.2.4", "prop-types": "^15.6.0", - "react": "^16.4.1", - "react-dom": "^16.4.1", + "react": "^16.7.0-alpha.2", + "react-dom": "^16.7.0-alpha.2", "react-helmet": "^5.2.0", - "react-hot-loader": "^4.3.12", - "react-redux": "^5.0.6", + "react-hot-loader": "^4.5.1", + "react-redux": "^6.0.0-beta.2", "react-router": "^4.3.1", "react-router-dom": "^4.3.1", "redux": "^4.0.0", diff --git a/src/containers/Home/FormPostTotalCount.js b/src/containers/Home/FormPostTotalCount.js new file mode 100644 index 0000000..9a713be --- /dev/null +++ b/src/containers/Home/FormPostTotalCount.js @@ -0,0 +1,20 @@ +import React/* , { useEffect } */ from 'react'; +import { useRedux } from '../../hooks/useRedux'; + +import style from './style.scss'; // eslint-disable-line no-unused-vars + +export default function FormPostTotalCount() { + const [state/* , dispatch */] = useRedux(); + return ( +
+ + this component uses react hook + +
+ Total  + {state.pages.home.posts.length} +  Posts +
+
+ ); +} diff --git a/src/containers/Home/index.js b/src/containers/Home/index.js index 714b57b..b3a59af 100644 --- a/src/containers/Home/index.js +++ b/src/containers/Home/index.js @@ -5,6 +5,7 @@ import { connect } from 'react-redux'; import { get as _get } from 'lodash'; import { dummy as dummyAct, fetchPosts as fetchPostsAction } from '../../actions'; import FormPostComponent from './FormPost'; +import FormPostTotalCount from './FormPostTotalCount'; import PostlistComponent from './Postlist'; import stdout from '../../stdout'; import style from './style.scss'; // eslint-disable-line no-unused-vars @@ -45,6 +46,7 @@ export class Home extends React.Component { +
    {posts.map(p => )} diff --git a/src/containers/Home/style.scss b/src/containers/Home/style.scss index 20214df..baefae8 100644 --- a/src/containers/Home/style.scss +++ b/src/containers/Home/style.scss @@ -3,4 +3,21 @@ width: 70%; margin: 0 auto; } + + .posts__total { + margin-left: 15%; + margin-bottom: 1rem; + border: 1px solid #eeeeee; + display: flex; + width: 30%; + padding: 1rem; + } + + .posts__small { + font-size: 0.5rem; + background-color: #202860; + color: white; + padding: 0.1rem 0.2rem; + margin-right: 1rem; + } } diff --git a/src/hooks/useRedux.js b/src/hooks/useRedux.js new file mode 100644 index 0000000..3e586a2 --- /dev/null +++ b/src/hooks/useRedux.js @@ -0,0 +1,11 @@ +// react is very likely to provide an official hook for redux +// https://reactjs.org/docs/hooks-faq.html#what-do-hooks-mean-for-popular-apis-like-redux-connect-and-react-router +import { useContext } from 'react'; +import { ReactReduxContext } from 'react-redux'; + +export function useRedux() { + const { storeState: state, store: { dispatch } } = useContext(ReactReduxContext); + return [state, dispatch]; +} + +export default useRedux; diff --git a/yarn.lock b/yarn.lock index 25bb45c..f620c32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2990,6 +2990,10 @@ eslint-plugin-jsx-a11y@^6.1.1: has "^1.0.3" jsx-ast-utils "^2.0.1" +eslint-plugin-react-hooks@^0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-0.0.0.tgz#9988f14082a159931c3dfa9ba699130457da927a" + eslint-plugin-react@^7.11.1: version "7.11.1" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz#c01a7af6f17519457d6116aa94fc6d2ccad5443c" @@ -3854,7 +3858,7 @@ hoist-non-react-statics@^2.5.0: version "2.5.5" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" -hoist-non-react-statics@^3.1.0: +hoist-non-react-statics@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.1.0.tgz#42414ccdfff019cd2168168be998c7b3bd5245c0" dependencies: @@ -6623,14 +6627,14 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dom@^16.4.1: - version "16.6.3" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.6.3.tgz#8fa7ba6883c85211b8da2d0efeffc9d3825cccc0" +react-dom@^16.7.0-alpha.2: + version "16.7.0-alpha.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0-alpha.2.tgz#16632880ed43676315991d8b412cce6975a30282" dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.11.2" + scheduler "^0.12.0-alpha.2" react-helmet@^5.2.0: version "5.2.0" @@ -6641,36 +6645,38 @@ react-helmet@^5.2.0: prop-types "^15.5.4" react-side-effect "^1.1.0" -react-hot-loader@^4.3.12: - version "4.3.12" - resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.3.12.tgz#0d56688884e7330c63a00a17217866280616b07a" +react-hot-loader@^4.5.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.5.1.tgz#4a9cf136542b1db4f948ac74b31fa16a411ceebb" dependencies: fast-levenshtein "^2.0.6" global "^4.3.0" hoist-non-react-statics "^2.5.0" + loader-utils "^1.1.0" + lodash.merge "^4.6.1" prop-types "^15.6.1" react-lifecycles-compat "^3.0.4" shallowequal "^1.0.2" + source-map "^0.7.3" react-is@^16.3.2, react-is@^16.6.0, react-is@^16.6.1, react-is@^16.6.3: version "16.6.3" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0" -react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" -react-redux@^5.0.6: - version "5.1.1" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.1.tgz#88e368682c7fa80e34e055cd7ac56f5936b0f52f" +react-redux@^6.0.0-beta.2: + version "6.0.0-beta.2" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-6.0.0-beta.2.tgz#90b8d50ae71d9dc5d413200836d279de64f7c3e2" dependencies: "@babel/runtime" "^7.1.2" - hoist-non-react-statics "^3.1.0" + hoist-non-react-statics "^3.0.1" invariant "^2.2.4" loose-envify "^1.1.0" - prop-types "^15.6.1" + prop-types "^15.6.2" react-is "^16.6.0" - react-lifecycles-compat "^3.0.0" react-router-dom@^4.3.1: version "4.3.1" @@ -6711,14 +6717,14 @@ react-test-renderer@^16.0.0-0: react-is "^16.6.3" scheduler "^0.11.2" -react@^16.4.1: - version "16.6.3" - resolved "https://registry.yarnpkg.com/react/-/react-16.6.3.tgz#25d77c91911d6bbdd23db41e70fb094cc1e0871c" +react@^16.7.0-alpha.2: + version "16.7.0-alpha.2" + resolved "https://registry.yarnpkg.com/react/-/react-16.7.0-alpha.2.tgz#924f2ae843a46ea82d104a8def7a599fbf2c78ce" dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.11.2" + scheduler "^0.12.0-alpha.2" read-pkg-up@^1.0.1: version "1.0.1" @@ -7172,6 +7178,13 @@ scheduler@^0.11.2: loose-envify "^1.1.0" object-assign "^4.1.1" +scheduler@^0.12.0-alpha.2: + version "0.12.0-alpha.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.12.0-alpha.2.tgz#2a8bc8dc6ecdb75fa6480ceeedc1f187c9539970" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-utils@^0.4.4: version "0.4.7" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" @@ -7441,6 +7454,10 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + spawn-wrap@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.2.tgz#cff58e73a8224617b6561abdc32586ea0c82248c" From 69fea867d29e183514405cc02b6b88c6b27137c6 Mon Sep 17 00:00:00 2001 From: ddhp Date: Fri, 23 Nov 2018 19:07:39 +0900 Subject: [PATCH 3/9] dispatch dummyaction in hook --- src/actions/index.js | 8 +++++--- src/containers/Home/FormPostTotalCount.js | 14 +++++++++++++- src/containers/Home/style.scss | 7 ++++++- src/reducers/pages/home.js | 9 +++++++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/actions/index.js b/src/actions/index.js index 7ca8cc6..21a490b 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -11,9 +11,11 @@ export function accumulateCount() { } export function dummy() { - return { - type: 'DUMMY_ACTION', - }; + return async dispatch => new Promise(() => setTimeout(() => { + dispatch({ + type: 'DUMMY_ACTION', + }); + }, 1000)); } export function updateMe(me) { diff --git a/src/containers/Home/FormPostTotalCount.js b/src/containers/Home/FormPostTotalCount.js index 9a713be..e3679cf 100644 --- a/src/containers/Home/FormPostTotalCount.js +++ b/src/containers/Home/FormPostTotalCount.js @@ -1,10 +1,15 @@ import React/* , { useEffect } */ from 'react'; import { useRedux } from '../../hooks/useRedux'; +import { dummy as dummyAction } from '../../actions'; import style from './style.scss'; // eslint-disable-line no-unused-vars export default function FormPostTotalCount() { - const [state/* , dispatch */] = useRedux(); + const [state, dispatch] = useRedux(); + const triggerDummy = () => { + dispatch(dummyAction()); + }; + return (
    @@ -15,6 +20,13 @@ export default function FormPostTotalCount() { {state.pages.home.posts.length}  Posts
    +
    + {state.pages.home.howManyDummies} +  times dummy +
    + ); } diff --git a/src/containers/Home/style.scss b/src/containers/Home/style.scss index baefae8..7165036 100644 --- a/src/containers/Home/style.scss +++ b/src/containers/Home/style.scss @@ -9,7 +9,7 @@ margin-bottom: 1rem; border: 1px solid #eeeeee; display: flex; - width: 30%; + width: 70%; padding: 1rem; } @@ -20,4 +20,9 @@ padding: 0.1rem 0.2rem; margin-right: 1rem; } + + .posts__dummy { + margin-left: 5rem; + margin-right: 1rem; + } } diff --git a/src/reducers/pages/home.js b/src/reducers/pages/home.js index 2387eef..4ca2124 100644 --- a/src/reducers/pages/home.js +++ b/src/reducers/pages/home.js @@ -7,6 +7,7 @@ const debug = stdout('reducer:home'); const initialState = { count: 0, posts: [], + howManyDummies: 0, }; export default function homeReducer(state = initialState, action) { @@ -43,6 +44,14 @@ export default function homeReducer(state = initialState, action) { return state; } + case 'DUMMY_ACTION': { + return update(state, { + howManyDummies: { + $set: state.howManyDummies + 1, + }, + }); + } + default: return state; } From c392ae6f8d5fe5bf5d9634cef2bc4c67de8536aa Mon Sep 17 00:00:00 2001 From: ddhp Date: Fri, 23 Nov 2018 19:14:25 +0900 Subject: [PATCH 4/9] useDummy hook --- src/containers/Home/FormPostTotalCount.js | 10 ++++------ src/hooks/useDummy.js | 10 ++++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 src/hooks/useDummy.js diff --git a/src/containers/Home/FormPostTotalCount.js b/src/containers/Home/FormPostTotalCount.js index e3679cf..bde78ee 100644 --- a/src/containers/Home/FormPostTotalCount.js +++ b/src/containers/Home/FormPostTotalCount.js @@ -1,14 +1,12 @@ import React/* , { useEffect } */ from 'react'; import { useRedux } from '../../hooks/useRedux'; -import { dummy as dummyAction } from '../../actions'; +import useDummy from '../../hooks/useDummy'; import style from './style.scss'; // eslint-disable-line no-unused-vars export default function FormPostTotalCount() { - const [state, dispatch] = useRedux(); - const triggerDummy = () => { - dispatch(dummyAction()); - }; + const [state] = useRedux(); + const [dummyTimes, triggerDummy] = useDummy(); return (
    @@ -21,7 +19,7 @@ export default function FormPostTotalCount() {  Posts
    - {state.pages.home.howManyDummies} + {dummyTimes}  times dummy