From dadc2e2ed203b8749b6ba4b91fd7a1fd45a300ee Mon Sep 17 00:00:00 2001 From: Matej Lauko Date: Wed, 5 Jul 2017 00:47:13 +0200 Subject: [PATCH 1/2] Add mobx Adds mobx base for browser & server + sample store with example usage in component --- .gitignore | 1 + package.json | 4 ++ src/browser/App.react.js | 49 ++++++++++++++++----- src/browser/BrowserProvider.react.js | 32 +++++++++++++- src/browser/store.js | 19 ++++++++ src/common/createStores.js | 43 ++++++++++++++++++ src/server/frontend/Html.react.js | 9 +++- src/server/frontend/ServerProvider.react.js | 20 ++++++++- src/server/frontend/render.js | 10 ++++- yarn.lock | 22 +++++++++ 10 files changed, 194 insertions(+), 15 deletions(-) create mode 100644 src/browser/store.js create mode 100644 src/common/createStores.js diff --git a/.gitignore b/.gitignore index 85ab422..8212c6c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ cypress/videos # IDE .idea *.sublime-workspace +.vscode diff --git a/package.json b/package.json index 04afff5..7b81349 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "eslint-plugin-jsx-a11y": "^3.0.2", "eslint-plugin-react": "^6.9.0", "jest": "^18.1.0", + "mobx-react-devtools": "^4.2.15", "react-addons-test-utils": "^15.4.2" }, "dependencies": { @@ -66,6 +67,8 @@ "file-loader": "^0.10.1", "helmet": "^3.4.0", "html-webpack-plugin": "^2.28.0", + "mobx": "^3.1.16", + "mobx-react": "^4.2.1", "morgan": "^1.7.0", "pretty-error": "^2.0.2", "react": "^15.4.2", @@ -74,6 +77,7 @@ "react-helmet": "^4.0.0", "rimraf": "^2.6.1", "rollbar": "^0.6.5", + "serialize-javascript": "^1.3.0", "source-map-support": "^0.4.9", "spdy": "^3.4.4", "url-loader": "^0.5.8", diff --git a/src/browser/App.react.js b/src/browser/App.react.js index 04b1924..12031bf 100644 --- a/src/browser/App.react.js +++ b/src/browser/App.react.js @@ -1,15 +1,44 @@ +import React, { Component, PropTypes as RPT } from 'react'; import Helmet from 'react-helmet'; -import React from 'react'; +import { observer, inject } from 'mobx-react'; import logo from '../../assets/images/haystack_logo.png'; -const App = () => ( -
- - -
-); +@inject('sample') +@observer +class App extends Component { + handlePlus = () => this.props.sample.plus() + handleMinus = () => this.props.sample.minus() + + render() { + const { sample } = this.props; + + return ( +
+ + + +

+ + + + +

{sample.count}

+ + +
+ ); + } +} + +App.wrappedComponent.propTypes = { + sample: RPT.shape({ + count: RPT.number.isRequired, + plus: RPT.func.isRequired, + minus: RPT.func.isRequired, + }).isRequired +}; export default App; diff --git a/src/browser/BrowserProvider.react.js b/src/browser/BrowserProvider.react.js index 1e02ada..9c7c967 100644 --- a/src/browser/BrowserProvider.react.js +++ b/src/browser/BrowserProvider.react.js @@ -1,3 +1,33 @@ -const BrowserProvider = ({ children }) => children; +import React, { PropTypes as RPT } from 'react'; +import { Provider } from 'mobx-react'; +import createStores from '../common/createStores'; + +const stores = createStores(window.MOBX_STATE); + +let AppRoot = null; + +// Use mobx-react-devtools in dev build +if (process.env.APP_ENV === 'development') { + const DevTools = require('mobx-react-devtools').default; // eslint-disable-line import/no-extraneous-dependencies, global-require + + AppRoot = children => ( +
+ {children} + +
+ ); +} else { + AppRoot = children => children; +} + +const BrowserProvider = ({ children }) => ( + + {AppRoot(children)} + +); + +BrowserProvider.propTypes = { + children: RPT.node.isRequired +}; export default BrowserProvider; diff --git a/src/browser/store.js b/src/browser/store.js new file mode 100644 index 0000000..53325ae --- /dev/null +++ b/src/browser/store.js @@ -0,0 +1,19 @@ +import { action, observable, computed } from 'mobx'; + +export default class SampleStore { + @observable count = 0; + + @action plus() { + this.count += 1; + } + + @action minus() { + this.count -= 1; + } + + @computed get special() { + return this.count < 5 + ? '¯\\_(ツ)_/¯' + : '•_•)
( •_•)>⌐■-■
(⌐■_■)'; + } +} diff --git a/src/common/createStores.js b/src/common/createStores.js new file mode 100644 index 0000000..602dac5 --- /dev/null +++ b/src/common/createStores.js @@ -0,0 +1,43 @@ +import { toJS, extendObservable } from 'mobx'; +import SampleStore from '../browser/store'; + +const createAndHydrateStore = (Store, initialData) => { + let store = new Store(); + if (initialData) { + store = extendObservable(store, initialData); + } + return store; +}; + +const createNewStores = (stores, initialData) => + Object.keys(stores).reduce( + (finalStores, storeKey) => ({ + ...finalStores, + [storeKey]: createAndHydrateStore(stores[storeKey], initialData[storeKey]), + }), + stores + ); + +const createStores = (initialData = {}) => { + /** + * Define the stores + * e.g { + * todos: Todos, + * user: User + * } + */ + const stores = { + sample: SampleStore + }; + + return createNewStores(stores, initialData); +}; + +export const getState = stores => + Object.keys(stores).reduce((data, storeKey) => ({ + ...data, + [storeKey]: toJS(stores[storeKey]) + }), {}); + + +export default createStores; diff --git a/src/server/frontend/Html.react.js b/src/server/frontend/Html.react.js index 5cafaac..121fe4e 100644 --- a/src/server/frontend/Html.react.js +++ b/src/server/frontend/Html.react.js @@ -1,10 +1,11 @@ /* eslint-disable react/no-danger */ import React, { PropTypes as RPT } from 'react'; +import serialize from 'serialize-javascript'; import Rollbar from './scripts/Rollbar'; import Script from './Script.react'; import { googleTagManagerNoScript, googleTagManagerScript } from './scripts/GoogleTagManager'; -const Html = ({ bodyHtml, javascripts = {}, helmet, options }) => ( +const Html = ({ bodyHtml, javascripts = {}, helmet, options, mobxState }) => ( {googleTagManagerScript()} @@ -24,6 +25,7 @@ const Html = ({ bodyHtml, javascripts = {}, helmet, options }) => ( {googleTagManagerNoScript()}
"`; +exports[`test render 1`] = `"
"`;