From 1a3e79e086dcbe41a5fd7a85991ffec12c895867 Mon Sep 17 00:00:00 2001 From: "Tom.Huang" Date: Sat, 29 Jun 2019 06:35:11 +0800 Subject: [PATCH 1/6] feat(mobx): mobx hooks --- packages/taro-mobx-common/src/index.js | 8 ++- packages/taro-mobx-common/src/inject.js | 29 ++------ packages/taro-mobx-common/src/observer.js | 68 ++++++++----------- .../taro-mobx-common/src/staticRendering.js | 9 +++ .../src/useAsObservableSource.js | 29 ++++++++ .../taro-mobx-common/src/useLocalStore.js | 27 ++++++++ packages/taro-mobx-common/src/utils.js | 13 ++++ packages/taro-mobx-h5/package.json | 4 +- packages/taro-mobx-h5/rollup.config.js | 2 +- packages/taro-mobx-h5/src/Provider.js | 0 packages/taro-mobx-h5/src/index.js | 45 +++++++++--- packages/taro-mobx-h5/src/inject.js | 10 ++- packages/taro-mobx-rn/package.json | 2 +- packages/taro-mobx-rn/src/Provider.js | 0 packages/taro-mobx-rn/src/index.js | 46 ++++++++++--- packages/taro-mobx-rn/src/inject.js | 26 +++++-- packages/taro-mobx/package.json | 6 +- packages/taro-mobx/rollup.config.js | 2 +- packages/taro-mobx/src/index.js | 40 ++++++++--- packages/taro-mobx/src/inject.js | 14 ++-- packages/taro-mobx/types/index.d.ts | 20 ++++++ 21 files changed, 289 insertions(+), 111 deletions(-) mode change 100755 => 100644 packages/taro-mobx-common/src/observer.js create mode 100644 packages/taro-mobx-common/src/staticRendering.js create mode 100644 packages/taro-mobx-common/src/useAsObservableSource.js create mode 100644 packages/taro-mobx-common/src/useLocalStore.js create mode 100644 packages/taro-mobx-common/src/utils.js mode change 100755 => 100644 packages/taro-mobx-h5/src/Provider.js mode change 100755 => 100644 packages/taro-mobx-rn/src/Provider.js create mode 100644 packages/taro-mobx/types/index.d.ts diff --git a/packages/taro-mobx-common/src/index.js b/packages/taro-mobx-common/src/index.js index 0a94c3e1eac5..d8711e88ff8a 100755 --- a/packages/taro-mobx-common/src/index.js +++ b/packages/taro-mobx-common/src/index.js @@ -2,6 +2,10 @@ import { errorsReporter } from './reporter' export const onError = fn => errorsReporter.on(fn) -export * from './inject' export { observer } from './observer' -export { getStore, setStore } from './store' \ No newline at end of file +export { useLocalStore } from './useLocalStore' +export { useAsObservableSource } from './useAsObservableSource' +export { isUsingStaticRendering, useStaticRendering } from './staticRendering' + +export { getStore, setStore } from './store' +export { inject, getInjectName, mapStoreToProps } from './inject' \ No newline at end of file diff --git a/packages/taro-mobx-common/src/inject.js b/packages/taro-mobx-common/src/inject.js index 77b8d57f9116..974ca6e9bc8d 100755 --- a/packages/taro-mobx-common/src/inject.js +++ b/packages/taro-mobx-common/src/inject.js @@ -17,31 +17,16 @@ function grabStoresByName (storeNames) { } } -export function generateDisplayName (sourceComponent, injectNames) { - let displayName = - 'inject-' + - (sourceComponent.displayName || - sourceComponent.name || - (sourceComponent.constructor && sourceComponent.constructor.name) || - 'Unknown') - if (injectNames) { - displayName += '-with-' + injectNames - } - return displayName +export function getInjectName(component, injectNames) { + const componentName = component.displayName || component.name || 'Component' + if (injectNames) { + return `inject-with-${injectNames}(${componentName})` + } + return `inject(${componentName})` } export function mapStoreToProps (grabStoresFn, props) { - let newProps = {} - for (let key in props) { - if (props.hasOwnProperty(key)) { - newProps[key] = props[key] - } - } - const additionalProps = grabStoresFn(getStore() || {}, newProps) || {} - for (let key in additionalProps) { - newProps[key] = additionalProps[key] - } - return newProps + return Object.assign({}, grabStoresFn(getStore() || {}, props || {}) || {}) } export function inject (/* fn(stores, nextProps) or ...storeNames, createStoreInjector */) { diff --git a/packages/taro-mobx-common/src/observer.js b/packages/taro-mobx-common/src/observer.js old mode 100755 new mode 100644 index 41867dbccd40..1cc5181f1b6e --- a/packages/taro-mobx-common/src/observer.js +++ b/packages/taro-mobx-common/src/observer.js @@ -1,56 +1,46 @@ -import { Reaction, _allowStateChanges } from 'mobx' +import { Reaction } from 'mobx' +import { isMiniPlatform } from './utils' import { errorsReporter } from './reporter' +import { isUsingStaticRendering } from './staticRendering' -function isStateless (component, renderMedthod) { - return !(component.prototype && component.prototype[renderMedthod]) -} - -export function observer (Component, renderMedthod) { - if (typeof Component !== 'function' || isStateless(Component, renderMedthod)) { - throw new Error("Please pass a valid component to 'observer'") +export function observer (component) { + if (isUsingStaticRendering()) { + return component } - if (Component.isMobxInjector === true) { + if (component.isMobxInjector === true) { console.warn( "Mobx observer: You are trying to use 'observer' on a component that already has 'inject'. Please apply 'observer' before applying 'inject'" ) } - class ObserverComponent extends Component { - componentWillMount () { - const initialName = - this.displayName || - this.name || - (this.constructor && (this.constructor.displayName || this.constructor.name)) || - '' - this._reaction = new Reaction(`${initialName}_${Date.now()}`, () => { - if (typeof this.componentWillReact === 'function') { - this.componentWillReact() - } - this.forceUpdate() - }) - if (typeof super.componentWillMount === 'function') { - super.componentWillMount() - } - } + const target = component.prototype + const originComponentWillMount = target.componentWillMount + const originComponentWillReact = target.componentWillReact + target.componentWillMount = function () { + const initialName = this.displayName || this.name + this._reaction = new Reaction(`${initialName}_${Date.now()}`, () => { + this.forceUpdate() + originComponentWillReact && originComponentWillReact.call(this) + }) + originComponentWillMount && originComponentWillMount.call(this) + } - componentWillUnmount () { - this._reaction.dispose() - if (typeof super.componentWillUnmount === 'function') { - super.componentWillUnmount() - } - } + const originComponentWillUnmount = target.componentWillUnmount + target.componentWillUnmount = function () { + this._reaction.dispose() + originComponentWillUnmount && originComponentWillUnmount.call(this) } - const target = ObserverComponent.prototype - const originRender = target[renderMedthod] - target[renderMedthod] = function (...args) { + const renderMethod = isMiniPlatform() ? '_createData' : 'render' + const originRender = target[renderMethod] + target[renderMethod] = function (...args) { let result let exception - if (this._reaction && this._reaction instanceof Reaction) { + if (this._reaction instanceof Reaction) { this._reaction.track(() => { try { - result = _allowStateChanges(false, () => originRender.call(this, null, null, args[2])) + result = originRender.call(this, null, null, args[2]) } catch (e) { exception = e } @@ -65,5 +55,5 @@ export function observer (Component, renderMedthod) { return result } - return ObserverComponent -} + return component +} \ No newline at end of file diff --git a/packages/taro-mobx-common/src/staticRendering.js b/packages/taro-mobx-common/src/staticRendering.js new file mode 100644 index 000000000000..598f0f4a4f71 --- /dev/null +++ b/packages/taro-mobx-common/src/staticRendering.js @@ -0,0 +1,9 @@ +let globalIsUsingStaticRendering = false + +export function useStaticRendering (enable) { + globalIsUsingStaticRendering = enable +} + +export function isUsingStaticRendering () { + return globalIsUsingStaticRendering +} \ No newline at end of file diff --git a/packages/taro-mobx-common/src/useAsObservableSource.js b/packages/taro-mobx-common/src/useAsObservableSource.js new file mode 100644 index 000000000000..f2b6bd42599b --- /dev/null +++ b/packages/taro-mobx-common/src/useAsObservableSource.js @@ -0,0 +1,29 @@ +import { observable, runInAction } from 'mobx' +import { isPlainObject } from './utils' + +export function useAsObservableSourceInternal (current, usedByLocalStore, useState) { + const culprit = usedByLocalStore ? 'useLocalStore' : 'useAsObservableSource' + if (usedByLocalStore && current === undefined) { + return undefined + } + if (process.env.NODE_ENV !== "production" && !isPlainObject(current)) { + throw new Error( + `${culprit} expects a plain object as ${usedByLocalStore ? "second" : "first"} argument` + ) + } + const [res] = useState(() => observable(current, {}, { deep: false })) + if ( + process.env.NODE_ENV !== "production" && + Object.keys(res).length !== Object.keys(current).length + ) { + throw new Error(`the shape of objects passed to ${culprit} should be stable`) + } + runInAction(() => { + Object.assign(res, current) + }) + return res +} + +export function useAsObservableSource (current, useState) { + return useAsObservableSourceInternal(current, false, useState) +} \ No newline at end of file diff --git a/packages/taro-mobx-common/src/useLocalStore.js b/packages/taro-mobx-common/src/useLocalStore.js new file mode 100644 index 000000000000..06824ee5cf38 --- /dev/null +++ b/packages/taro-mobx-common/src/useLocalStore.js @@ -0,0 +1,27 @@ +import { observable, transaction, runInAction } from 'mobx' +import { useAsObservableSourceInternal } from './useAsObservableSource' +import { isPlainObject } from './utils' + +export function useLocalStore (initializer, current, useState) { + const source = useAsObservableSourceInternal(current, true, useState) + return useState(() => { + const local = observable(initializer(source)) + if (isPlainObject(local)) { + runInAction(() => { + Object.keys(local).forEach(key => { + const value = local[key] + if (typeof value === "function") { + local[key] = wrapInTransaction(value, local) + } + }) + }) + } + return local + })[0] +} + +function wrapInTransaction (fn, context) { + return (...args) => { + return transaction(() => fn.apply(context, args)) + } +} \ No newline at end of file diff --git a/packages/taro-mobx-common/src/utils.js b/packages/taro-mobx-common/src/utils.js new file mode 100644 index 000000000000..774b8e256eba --- /dev/null +++ b/packages/taro-mobx-common/src/utils.js @@ -0,0 +1,13 @@ +import Taro from '@tarojs/taro' + +export function isPlainObject (value) { + if (!value || typeof value !== 'object') { + return false + } + const proto = Object.getPrototypeOf(value) + return !proto || proto === Object.prototype +} + +export function isMiniPlatform () { + return !(/^WEB|RN$/i.test(Taro.getEnv())) +} \ No newline at end of file diff --git a/packages/taro-mobx-h5/package.json b/packages/taro-mobx-h5/package.json index 3c15ff5b407e..718a042fbc8e 100644 --- a/packages/taro-mobx-h5/package.json +++ b/packages/taro-mobx-h5/package.json @@ -25,11 +25,11 @@ "index.js" ], "peerDependencies": { - "@tarojs/taro-h5": "1.2.17", "nervjs": "^1.4.0" }, "dependencies": { - "@tarojs/mobx-common": "1.3.5" + "@tarojs/mobx-common": "1.3.5", + "@tarojs/taro-h5": "1.3.5" }, "devDependencies": { "rimraf": "^2.6.2", diff --git a/packages/taro-mobx-h5/rollup.config.js b/packages/taro-mobx-h5/rollup.config.js index 3862781ce145..1b9ff63016be 100644 --- a/packages/taro-mobx-h5/rollup.config.js +++ b/packages/taro-mobx-h5/rollup.config.js @@ -14,7 +14,7 @@ export default { format: 'esm', file: 'dist/index.esm.js' }], - external: ['nervjs', 'mobx', '@tarojs/taro-h5', '@tarojs/mobx-common'], + external: ['@tarojs/taro-h5', '@tarojs/mobx-common'], plugins: [ babel({ presets: [ diff --git a/packages/taro-mobx-h5/src/Provider.js b/packages/taro-mobx-h5/src/Provider.js old mode 100755 new mode 100644 diff --git a/packages/taro-mobx-h5/src/index.js b/packages/taro-mobx-h5/src/index.js index b2f7ceb9af7e..9ebe5653ca09 100644 --- a/packages/taro-mobx-h5/src/index.js +++ b/packages/taro-mobx-h5/src/index.js @@ -1,14 +1,43 @@ -import { createStoreInjector } from './inject' -import { observer as originObserver, inject as originInject } from '@tarojs/mobx-common' +import { useState } from '@tarojs/taro-h5' -export function inject () { - return originInject(...arguments, createStoreInjector) +import { + onError, + observer, + isUsingStaticRendering, + useStaticRendering, + useLocalStore as originUseLocalStore, + useAsObservableSource as originUseAsObservableSource, +} from '@tarojs/mobx-common' + +import Provider from './Provider' +import { inject } from './inject' + +function useLocalStore (initializer, current) { + return originUseLocalStore(initializer, current, useState) } -export function observer (Component) { - return originObserver(Component, 'render') +function useAsObservableSource (current) { + return originUseAsObservableSource(current, useState) } -export { onError } from '@tarojs/mobx-common' +export default { + onError, + observer, + inject, + Provider, + useLocalStore, + useAsObservableSource, + isUsingStaticRendering, + useStaticRendering +} -export { default as Provider } from './Provider' \ No newline at end of file +export { + onError, + observer, + inject, + Provider, + useLocalStore, + useAsObservableSource, + isUsingStaticRendering, + useStaticRendering +} \ No newline at end of file diff --git a/packages/taro-mobx-h5/src/inject.js b/packages/taro-mobx-h5/src/inject.js index be38b21f6057..7e09864f188d 100644 --- a/packages/taro-mobx-h5/src/inject.js +++ b/packages/taro-mobx-h5/src/inject.js @@ -1,12 +1,12 @@ import { createElement } from 'nervjs' import { Component } from '@tarojs/taro-h5' -import { mapStoreToProps, generateDisplayName } from '@tarojs/mobx-common' +import { mapStoreToProps, getInjectName, inject as originInject } from '@tarojs/mobx-common' -export function createStoreInjector (grabStoresFn, injectNames, sourceComponent) { +function createStoreInjector (grabStoresFn, injectNames, sourceComponent) { class Injector extends Component { static isMobxInjector = true static config = sourceComponent.config || {} - static displayName = generateDisplayName(sourceComponent, injectNames) + static displayName = getInjectName(sourceComponent, injectNames) __observeInstance render () { @@ -39,3 +39,7 @@ export function createStoreInjector (grabStoresFn, injectNames, sourceComponent) return Injector } + +export function inject () { + return originInject(...arguments, createStoreInjector) +} diff --git a/packages/taro-mobx-rn/package.json b/packages/taro-mobx-rn/package.json index 676e52a5aeec..bdc73ef9de02 100644 --- a/packages/taro-mobx-rn/package.json +++ b/packages/taro-mobx-rn/package.json @@ -19,6 +19,6 @@ ], "dependencies": { "@tarojs/mobx-common": "1.3.5", - "@tarojs/taro-rn": "1.1.7" + "@tarojs/taro-rn": "1.3.5" } } diff --git a/packages/taro-mobx-rn/src/Provider.js b/packages/taro-mobx-rn/src/Provider.js old mode 100755 new mode 100644 diff --git a/packages/taro-mobx-rn/src/index.js b/packages/taro-mobx-rn/src/index.js index c6228330edd7..e7881196f976 100644 --- a/packages/taro-mobx-rn/src/index.js +++ b/packages/taro-mobx-rn/src/index.js @@ -1,13 +1,43 @@ -import { createStoreInjector } from './inject' -import { observer as originObserver, inject as originInject } from '@tarojs/mobx-common' +import { useState } from '@tarojs/taro-rn' -export function inject () { - return originInject(...arguments, createStoreInjector) +import { + onError, + observer, + isUsingStaticRendering, + useStaticRendering, + useLocalStore as originUseLocalStore, + useAsObservableSource as originUseAsObservableSource, +} from '@tarojs/mobx-common' + +import Provider from './Provider' +import { inject } from './inject' + +function useLocalStore (initializer, current) { + return originUseLocalStore(initializer, current, useState) +} + +function useAsObservableSource (current) { + return originUseAsObservableSource(current, useState) } -export function observer (Component) { - return originObserver(Component, 'render') +export default { + onError, + observer, + inject, + Provider, + useLocalStore, + useAsObservableSource, + isUsingStaticRendering, + useStaticRendering } -export { onError } from '@tarojs/mobx-common' -export { default as Provider } from './Provider' \ No newline at end of file +export { + onError, + observer, + inject, + Provider, + useLocalStore, + useAsObservableSource, + isUsingStaticRendering, + useStaticRendering +} \ No newline at end of file diff --git a/packages/taro-mobx-rn/src/inject.js b/packages/taro-mobx-rn/src/inject.js index 4ef0e778c474..a6bc83c7d5d6 100644 --- a/packages/taro-mobx-rn/src/inject.js +++ b/packages/taro-mobx-rn/src/inject.js @@ -1,31 +1,45 @@ import { createElement } from 'react' import { Component } from '@tarojs/taro-rn' -import { mapStoreToProps, generateDisplayName } from '@tarojs/mobx-common' +import { mapStoreToProps, getInjectName, inject as originInject } from '@tarojs/mobx-common' -export function createStoreInjector (grabStoresFn, injectNames, sourceComponent) { +function createStoreInjector (grabStoresFn, injectNames, sourceComponent) { class Injector extends Component { static isMobxInjector = true static config = sourceComponent.config || {} - static displayName = generateDisplayName(sourceComponent, injectNames) + static displayName = getInjectName(sourceComponent, injectNames) + __observeInstance render () { - return createElement(sourceComponent, mapStoreToProps(grabStoresFn, this.props)) + const originProps = mapStoreToProps(grabStoresFn, this.props) + return createElement(sourceComponent, { + ...originProps, + ref: ref => { + originProps.ref && originProps.ref(ref) + if (ref) { + this.__observeInstance = ref + } + } + }) } componentDidShow () { const { componentDidShow } = sourceComponent.prototype if (typeof componentDidShow === 'function') { - componentDidShow() + componentDidShow.call(this.__observeInstance) } } componentDidHide () { const { componentDidHide } = sourceComponent.prototype if (typeof componentDidHide === 'function') { - componentDidHide() + componentDidHide.call(this.__observeInstance) } } } return Injector } + +export function inject () { + return originInject(...arguments, createStoreInjector) +} diff --git a/packages/taro-mobx/package.json b/packages/taro-mobx/package.json index 40ada9218884..61d67af2cbac 100644 --- a/packages/taro-mobx/package.json +++ b/packages/taro-mobx/package.json @@ -13,8 +13,10 @@ "url": "git+https://github.com/NervJS/taro.git" }, "main": "index.js", + "typings": "types/index.d.ts", "files": [ "dist", + "types", "index.js" ], "scripts": { @@ -22,11 +24,9 @@ "clear": "rimraf dist" }, "dependencies": { + "@tarojs/taro": "1.3.5", "@tarojs/mobx-common": "1.3.5" }, - "peerDependencies": { - "mobx": "4.8.0" - }, "devDependencies": { "rimraf": "^2.6.2", "rollup": "1.1.0", diff --git a/packages/taro-mobx/rollup.config.js b/packages/taro-mobx/rollup.config.js index 5498b6e4c9b6..67889371f6ec 100644 --- a/packages/taro-mobx/rollup.config.js +++ b/packages/taro-mobx/rollup.config.js @@ -9,7 +9,7 @@ export default { format: 'cjs', file: 'dist/index.js' }], - external: ['@tarojs/mobx-common'], + external: ['@tarojs/taro', '@tarojs/mobx-common'], plugins: [ babel({ presets: [ diff --git a/packages/taro-mobx/src/index.js b/packages/taro-mobx/src/index.js index ee3b2c504d35..d20e324e383e 100644 --- a/packages/taro-mobx/src/index.js +++ b/packages/taro-mobx/src/index.js @@ -1,30 +1,50 @@ -import { createStoreInjector } from './inject' -import { onError, getStore, setStore, observer as originObserver, inject as originInject } from '@tarojs/mobx-common' +import { useState } from '@tarojs/taro' + +import { + onError, + getStore, + setStore, + observer, + isUsingStaticRendering, + useStaticRendering, + useLocalStore as originUseLocalStore, + useAsObservableSource as originUseAsObservableSource, +} from '@tarojs/mobx-common' + +import { inject } from './inject' class Provider {} -function observer (Component) { - return originObserver(Component, '_createData') +function useLocalStore (initializer, current) { + return originUseLocalStore(initializer, current, useState) } -function inject () { - return originInject(...arguments, createStoreInjector) +function useAsObservableSource (current) { + return originUseAsObservableSource(current, useState) } export default { onError, getStore, setStore, - observer, inject, - Provider + observer, + Provider, + useLocalStore, + useAsObservableSource, + isUsingStaticRendering, + useStaticRendering } export { onError, getStore, setStore, - observer, inject, - Provider + observer, + Provider, + useLocalStore, + useAsObservableSource, + isUsingStaticRendering, + useStaticRendering } \ No newline at end of file diff --git a/packages/taro-mobx/src/inject.js b/packages/taro-mobx/src/inject.js index e95f99fcd7a5..dfe560e8f1f2 100644 --- a/packages/taro-mobx/src/inject.js +++ b/packages/taro-mobx/src/inject.js @@ -1,9 +1,9 @@ -import { mapStoreToProps, generateDisplayName } from '@tarojs/mobx-common' +import { mapStoreToProps, getInjectName, inject as originInject } from '@tarojs/mobx-common' -export function createStoreInjector (grabStoresFn, injectNames, Component) { +function createStoreInjector (grabStoresFn, injectNames, Component) { class Injector extends Component { static isMobxInjector = true - static displayName = generateDisplayName(Component, injectNames) + static displayName = getInjectName(Component, injectNames) constructor (props, isPage) { super(Object.assign(...arguments, mapStoreToProps(grabStoresFn, props)), isPage) } @@ -17,9 +17,13 @@ export function createStoreInjector (grabStoresFn, injectNames, Component) { } const target = Injector.prototype const originCreateData = target._createData - target._createData = function () { + target._createData = function (...args) { Object.assign(this.props, mapStoreToProps(grabStoresFn, this.props)) - return originCreateData.call(this) + return originCreateData.call(this, null, null, args[2]) } return Injector +} + +export function inject () { + return originInject(...arguments, createStoreInjector) } \ No newline at end of file diff --git a/packages/taro-mobx/types/index.d.ts b/packages/taro-mobx/types/index.d.ts new file mode 100644 index 000000000000..c33017b62794 --- /dev/null +++ b/packages/taro-mobx/types/index.d.ts @@ -0,0 +1,20 @@ +import Taro from '@tarojs/taro' + +export type IValueMap = { [key: string]: any }; + +export class Provider extends Taro.Component {}; + +export function onError(fn: (error: Error) => void); + +export function isUsingStaticRendering(): boolean; + +export function useStaticRendering(enable: boolean); +export function useLocalStore, TSource extends object = any>( + initializer: (source: TSource) => TStore, + current?: TSource +): TStore; +export function useAsObservableSource(current: TSource): TSource; + +export function observer(component); +export function inject(...stores: string[]); +export function inject(fn: (stores: IValueMap, nextProps: IValueMap) => IValueMap); \ No newline at end of file From ea903e5f1673d11bcc4b6f3c77a88211b5613944 Mon Sep 17 00:00:00 2001 From: "Tom.Huang" Date: Sat, 29 Jun 2019 06:36:40 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat(mobx):=20=E6=9B=B4=E6=96=B0=20mobx=20?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/taro-cli/templates/mobx/appjs | 11 +- packages/taro-cli/templates/mobx/index.js | 8 -- packages/taro-cli/templates/mobx/pagejs | 108 +++++------------- .../taro-cli/templates/mobx/store/counterjs | 20 ---- 4 files changed, 28 insertions(+), 119 deletions(-) delete mode 100644 packages/taro-cli/templates/mobx/store/counterjs diff --git a/packages/taro-cli/templates/mobx/appjs b/packages/taro-cli/templates/mobx/appjs index 9fd5bc8c8820..0b8570d27147 100644 --- a/packages/taro-cli/templates/mobx/appjs +++ b/packages/taro-cli/templates/mobx/appjs @@ -3,11 +3,8 @@ import Taro, { Component, Config } from '@tarojs/taro' <%} else { -%> import Taro, { Component } from '@tarojs/taro' <%}-%> -import { Provider } from '@tarojs/mobx' import Index from './pages/index' -import counterStore from './store/counter' - import './app.<%= css %>' // 如果需要在 h5 环境中开启 React Devtools @@ -16,10 +13,6 @@ import './app.<%= css %>' // require('nerv-devtools') // } -const store = { - counterStore -} - class App extends Component { <%if (locals.typescript) {-%> @@ -55,9 +48,7 @@ class App extends Component { // 请勿修改此函数 render () { return ( - - - + ) } } diff --git a/packages/taro-cli/templates/mobx/index.js b/packages/taro-cli/templates/mobx/index.js index c74ef2482c13..9f0ecad895df 100644 --- a/packages/taro-cli/templates/mobx/index.js +++ b/packages/taro-cli/templates/mobx/index.js @@ -133,11 +133,6 @@ exports.createApp = function (creator, params, helper, cb) { }) creator.template(template, path.join(configDirName, 'dev'), path.join(configDir, 'dev.js')) creator.template(template, path.join(configDirName, 'prod'), path.join(configDir, 'prod.js')) - if (typescript) { - creator.template(template, path.join('store', 'counterjs'), path.join(sourceDir, 'store', 'counter.ts')) - } else { - creator.template(template, path.join('store', 'counterjs'), path.join(sourceDir, 'store', 'counter.js')) - } if (useNpmrc) creator.template(template, 'npmrc', path.join(projectPath, '.npmrc')) if (useYarnLock) creator.template(template, yarnLockfilePath, path.join(projectPath, 'yarn.lock')) creator.fs.commit(() => { @@ -146,13 +141,10 @@ exports.createApp = function (creator, params, helper, cb) { console.log(`${chalk.green('✔ ')}${chalk.grey(`创建配置目录: ${projectName}/${configDirName}`)}`) console.log(`${chalk.green('✔ ')}${chalk.grey(`创建源码目录: ${projectName}/${src}`)}`) console.log(`${chalk.green('✔ ')}${chalk.grey(`创建页面目录: ${projectName}/${src}/pages`)}`) - console.log(`${chalk.green('✔ ')}${chalk.grey(`创建 store 目录: ${projectName}/${src}/store`)}`) if (typescript) { console.log(`${chalk.green('✔ ')}${chalk.grey(`创建页面 JS 文件: ${projectName}/${src}/pages/index/index.tsx`)}`) - console.log(`${chalk.green('✔ ')}${chalk.grey(`创建 store TS 文件: ${projectName}/${src}/store/counter.ts`)}`) } else { console.log(`${chalk.green('✔ ')}${chalk.grey(`创建页面 JS 文件: ${projectName}/${src}/pages/index/index.js`)}`) - console.log(`${chalk.green('✔ ')}${chalk.grey(`创建 store JS 文件: ${projectName}/${src}/store/counter.js`)}`) } console.log(`${chalk.green('✔ ')}${chalk.grey(`创建页面 ${currentStyleExt.toLocaleUpperCase()} 文件: ${projectName}/${src}/pages/index/${pageCSSName}`)}`) if (typescript) { diff --git a/packages/taro-cli/templates/mobx/pagejs b/packages/taro-cli/templates/mobx/pagejs index 47c6ab437d3b..78cd39f31def 100644 --- a/packages/taro-cli/templates/mobx/pagejs +++ b/packages/taro-cli/templates/mobx/pagejs @@ -1,86 +1,32 @@ -<%if (locals.typescript) {-%> -import { ComponentType } from 'react' -import Taro, { Component, Config } from '@tarojs/taro' -<%} else { -%> -import Taro, { Component } from '@tarojs/taro' -<%}-%> -import { View, Button, Text } from '@tarojs/components' -import { observer, inject } from '@tarojs/mobx' +import Taro from '@tarojs/taro' +import { View, Text, Button } from '@tarojs/components' +import { useLocalStore, observer } from '@tarojs/mobx' import './<%= pageName %>.<%= css %>' -<%if (locals.typescript) {-%> -type PageStateProps = { - counterStore: { - counter: number, - increment: Function, - decrement: Function, - incrementAsync: Function - } +function Index() { + const store = useLocalStore(() => ({ + counter: 0, + increment() { + store.counter++ + }, + decrement() { + store.counter-- + }, + incrementAsync() { + setTimeout(() => store.counter++, 1000) + } + })) + + const { counter, increment, decrement, incrementAsync } = store; + return ( + + + + + {counter} + + ) } -interface <%= _.capitalize(pageName) %> { - props: PageStateProps; -} -<%}-%> - -@inject('counterStore') -@observer -class <%= _.capitalize(pageName) %> extends Component { - -<%if (locals.typescript) {-%> - /** - * 指定config的类型声明为: Taro.Config - * - * 由于 typescript 对于 object 类型推导只能推出 Key 的基本类型 - * 对于像 navigationBarTextStyle: 'black' 这样的推导出的类型是 string - * 提示和声明 navigationBarTextStyle: 'black' | 'white' 类型冲突, 需要显示声明类型 - */ -<%}-%> - config<%if (locals.typescript) {%>: Config<%}%> = { - navigationBarTitleText: '首页' - } - - componentWillMount () { } - - componentWillReact () { - console.log('componentWillReact') - } - - componentDidMount () { } - - componentWillUnmount () { } - - componentDidShow () { } - - componentDidHide () { } - - increment = () => { - const { counterStore } = this.props - counterStore.increment() - } - - decrement = () => { - const { counterStore } = this.props - counterStore.decrement() - } - - incrementAsync = () => { - const { counterStore } = this.props - counterStore.incrementAsync() - } - - render () { - const { counterStore: { counter } } = this.props - return ( - - - - - {counter} - - ) - } -} - -export default <%= _.capitalize(pageName) %> <%if (locals.typescript) {%> as ComponentType<%}%> +export default observer(Index) \ No newline at end of file diff --git a/packages/taro-cli/templates/mobx/store/counterjs b/packages/taro-cli/templates/mobx/store/counterjs deleted file mode 100644 index bc2b1983ae2a..000000000000 --- a/packages/taro-cli/templates/mobx/store/counterjs +++ /dev/null @@ -1,20 +0,0 @@ -import { observable } from 'mobx' - -const counterStore = observable({ - counter: 0, - counterStore() { - this.counter++ - }, - increment() { - this.counter++ - }, - decrement() { - this.counter-- - }, - incrementAsync() { - setTimeout(() => { - this.counter++ - }, 1000) - } -}) -export default counterStore \ No newline at end of file From 396be300d6064dc446fd06f7dad4c55e611f46ae Mon Sep 17 00:00:00 2001 From: "Tom.Huang" Date: Sat, 29 Jun 2019 06:39:15 +0800 Subject: [PATCH 3/6] =?UTF-8?q?feat(mobx):=20=E5=88=A0=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/taro-cli/src/util/constants.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/taro-cli/src/util/constants.ts b/packages/taro-cli/src/util/constants.ts index 5b517ca3d805..9b1e6e5e54db 100644 --- a/packages/taro-cli/src/util/constants.ts +++ b/packages/taro-cli/src/util/constants.ts @@ -304,7 +304,6 @@ export const UPDATE_PACKAGE_LIST = [ '@tarojs/mobx-h5', '@tarojs/mobx-rn', '@tarojs/mobx-common', - '@tarojs/mobx-prop-types', 'nervjs', 'nerv-devtools' ] From 7da1500ecf7352473149233b7f3013ef89d29b68 Mon Sep 17 00:00:00 2001 From: "Tom.Huang" Date: Sat, 29 Jun 2019 07:07:19 +0800 Subject: [PATCH 4/6] feat(mobx): code style --- packages/taro-mobx-common/src/index.js | 2 +- packages/taro-mobx-common/src/inject.js | 4 ++-- packages/taro-mobx-common/src/observer.js | 4 ++-- packages/taro-mobx-common/src/reporter.js | 2 +- packages/taro-mobx-common/src/staticRendering.js | 2 +- packages/taro-mobx-common/src/useAsObservableSource.js | 8 ++++---- packages/taro-mobx-common/src/useLocalStore.js | 4 ++-- packages/taro-mobx-common/src/utils.js | 2 +- packages/taro-mobx-h5/src/index.js | 4 ++-- packages/taro-mobx-rn/src/index.js | 4 ++-- packages/taro-mobx/src/index.js | 4 ++-- packages/taro-mobx/src/inject.js | 2 +- 12 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/taro-mobx-common/src/index.js b/packages/taro-mobx-common/src/index.js index d8711e88ff8a..e7c9d86ab4a6 100755 --- a/packages/taro-mobx-common/src/index.js +++ b/packages/taro-mobx-common/src/index.js @@ -8,4 +8,4 @@ export { useAsObservableSource } from './useAsObservableSource' export { isUsingStaticRendering, useStaticRendering } from './staticRendering' export { getStore, setStore } from './store' -export { inject, getInjectName, mapStoreToProps } from './inject' \ No newline at end of file +export { inject, getInjectName, mapStoreToProps } from './inject' diff --git a/packages/taro-mobx-common/src/inject.js b/packages/taro-mobx-common/src/inject.js index 974ca6e9bc8d..4594e91e9f5a 100755 --- a/packages/taro-mobx-common/src/inject.js +++ b/packages/taro-mobx-common/src/inject.js @@ -17,7 +17,7 @@ function grabStoresByName (storeNames) { } } -export function getInjectName(component, injectNames) { +export function getInjectName (component, injectNames) { const componentName = component.displayName || component.name || 'Component' if (injectNames) { return `inject-with-${injectNames}(${componentName})` @@ -47,4 +47,4 @@ export function inject (/* fn(stores, nextProps) or ...storeNames, createStoreIn return createStoreInjector(grabStoresFn, storeNames.join('-'), componentClass) } } -} \ No newline at end of file +} diff --git a/packages/taro-mobx-common/src/observer.js b/packages/taro-mobx-common/src/observer.js index 1cc5181f1b6e..645b31e2034e 100644 --- a/packages/taro-mobx-common/src/observer.js +++ b/packages/taro-mobx-common/src/observer.js @@ -32,7 +32,7 @@ export function observer (component) { originComponentWillUnmount && originComponentWillUnmount.call(this) } - const renderMethod = isMiniPlatform() ? '_createData' : 'render' + const renderMethod = isMiniPlatform() ? '_createData' : 'render' const originRender = target[renderMethod] target[renderMethod] = function (...args) { let result @@ -56,4 +56,4 @@ export function observer (component) { } return component -} \ No newline at end of file +} diff --git a/packages/taro-mobx-common/src/reporter.js b/packages/taro-mobx-common/src/reporter.js index 920b6e8eae51..4685adc13318 100644 --- a/packages/taro-mobx-common/src/reporter.js +++ b/packages/taro-mobx-common/src/reporter.js @@ -16,4 +16,4 @@ class EventEmitter { } } -export const errorsReporter = new EventEmitter() \ No newline at end of file +export const errorsReporter = new EventEmitter() diff --git a/packages/taro-mobx-common/src/staticRendering.js b/packages/taro-mobx-common/src/staticRendering.js index 598f0f4a4f71..f309bc4c00fb 100644 --- a/packages/taro-mobx-common/src/staticRendering.js +++ b/packages/taro-mobx-common/src/staticRendering.js @@ -6,4 +6,4 @@ export function useStaticRendering (enable) { export function isUsingStaticRendering () { return globalIsUsingStaticRendering -} \ No newline at end of file +} diff --git a/packages/taro-mobx-common/src/useAsObservableSource.js b/packages/taro-mobx-common/src/useAsObservableSource.js index f2b6bd42599b..fcd55574d81f 100644 --- a/packages/taro-mobx-common/src/useAsObservableSource.js +++ b/packages/taro-mobx-common/src/useAsObservableSource.js @@ -6,14 +6,14 @@ export function useAsObservableSourceInternal (current, usedByLocalStore, useSta if (usedByLocalStore && current === undefined) { return undefined } - if (process.env.NODE_ENV !== "production" && !isPlainObject(current)) { + if (process.env.NODE_ENV !== 'production' && !isPlainObject(current)) { throw new Error( - `${culprit} expects a plain object as ${usedByLocalStore ? "second" : "first"} argument` + `${culprit} expects a plain object as ${usedByLocalStore ? 'second' : 'first'} argument` ) } const [res] = useState(() => observable(current, {}, { deep: false })) if ( - process.env.NODE_ENV !== "production" && + process.env.NODE_ENV !== 'production' && Object.keys(res).length !== Object.keys(current).length ) { throw new Error(`the shape of objects passed to ${culprit} should be stable`) @@ -26,4 +26,4 @@ export function useAsObservableSourceInternal (current, usedByLocalStore, useSta export function useAsObservableSource (current, useState) { return useAsObservableSourceInternal(current, false, useState) -} \ No newline at end of file +} diff --git a/packages/taro-mobx-common/src/useLocalStore.js b/packages/taro-mobx-common/src/useLocalStore.js index 06824ee5cf38..fa163c18d4f8 100644 --- a/packages/taro-mobx-common/src/useLocalStore.js +++ b/packages/taro-mobx-common/src/useLocalStore.js @@ -10,7 +10,7 @@ export function useLocalStore (initializer, current, useState) { runInAction(() => { Object.keys(local).forEach(key => { const value = local[key] - if (typeof value === "function") { + if (typeof value === 'function') { local[key] = wrapInTransaction(value, local) } }) @@ -24,4 +24,4 @@ function wrapInTransaction (fn, context) { return (...args) => { return transaction(() => fn.apply(context, args)) } -} \ No newline at end of file +} diff --git a/packages/taro-mobx-common/src/utils.js b/packages/taro-mobx-common/src/utils.js index 774b8e256eba..302d440c488a 100644 --- a/packages/taro-mobx-common/src/utils.js +++ b/packages/taro-mobx-common/src/utils.js @@ -10,4 +10,4 @@ export function isPlainObject (value) { export function isMiniPlatform () { return !(/^WEB|RN$/i.test(Taro.getEnv())) -} \ No newline at end of file +} diff --git a/packages/taro-mobx-h5/src/index.js b/packages/taro-mobx-h5/src/index.js index 9ebe5653ca09..c3f74f23c037 100644 --- a/packages/taro-mobx-h5/src/index.js +++ b/packages/taro-mobx-h5/src/index.js @@ -6,7 +6,7 @@ import { isUsingStaticRendering, useStaticRendering, useLocalStore as originUseLocalStore, - useAsObservableSource as originUseAsObservableSource, + useAsObservableSource as originUseAsObservableSource } from '@tarojs/mobx-common' import Provider from './Provider' @@ -40,4 +40,4 @@ export { useAsObservableSource, isUsingStaticRendering, useStaticRendering -} \ No newline at end of file +} diff --git a/packages/taro-mobx-rn/src/index.js b/packages/taro-mobx-rn/src/index.js index e7881196f976..4329afd9107c 100644 --- a/packages/taro-mobx-rn/src/index.js +++ b/packages/taro-mobx-rn/src/index.js @@ -6,7 +6,7 @@ import { isUsingStaticRendering, useStaticRendering, useLocalStore as originUseLocalStore, - useAsObservableSource as originUseAsObservableSource, + useAsObservableSource as originUseAsObservableSource } from '@tarojs/mobx-common' import Provider from './Provider' @@ -40,4 +40,4 @@ export { useAsObservableSource, isUsingStaticRendering, useStaticRendering -} \ No newline at end of file +} diff --git a/packages/taro-mobx/src/index.js b/packages/taro-mobx/src/index.js index d20e324e383e..8ef2ecebac2f 100644 --- a/packages/taro-mobx/src/index.js +++ b/packages/taro-mobx/src/index.js @@ -8,7 +8,7 @@ import { isUsingStaticRendering, useStaticRendering, useLocalStore as originUseLocalStore, - useAsObservableSource as originUseAsObservableSource, + useAsObservableSource as originUseAsObservableSource } from '@tarojs/mobx-common' import { inject } from './inject' @@ -47,4 +47,4 @@ export { useAsObservableSource, isUsingStaticRendering, useStaticRendering -} \ No newline at end of file +} diff --git a/packages/taro-mobx/src/inject.js b/packages/taro-mobx/src/inject.js index dfe560e8f1f2..85f106d133d3 100644 --- a/packages/taro-mobx/src/inject.js +++ b/packages/taro-mobx/src/inject.js @@ -26,4 +26,4 @@ function createStoreInjector (grabStoresFn, injectNames, Component) { export function inject () { return originInject(...arguments, createStoreInjector) -} \ No newline at end of file +} From 845dce76a6ad35ca4f1f259f286ce91b4ac38b2b Mon Sep 17 00:00:00 2001 From: "Tom.Huang" Date: Sat, 29 Jun 2019 07:22:05 +0800 Subject: [PATCH 5/6] =?UTF-8?q?feat(mobx):=20propTypes=20=E5=AE=9A?= =?UTF-8?q?=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/taro-mobx-common/src/index.js | 2 + packages/taro-mobx-common/src/propTypes.js | 187 +++++++++++++++++++++ packages/taro-mobx-h5/src/index.js | 3 + packages/taro-mobx-rn/src/index.js | 3 + packages/taro-mobx/src/index.js | 3 + packages/taro-mobx/types/index.d.ts | 16 +- 6 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 packages/taro-mobx-common/src/propTypes.js diff --git a/packages/taro-mobx-common/src/index.js b/packages/taro-mobx-common/src/index.js index e7c9d86ab4a6..462df39009d0 100755 --- a/packages/taro-mobx-common/src/index.js +++ b/packages/taro-mobx-common/src/index.js @@ -2,6 +2,8 @@ import { errorsReporter } from './reporter' export const onError = fn => errorsReporter.on(fn) +export { PropTypes } from "./propTypes" + export { observer } from './observer' export { useLocalStore } from './useLocalStore' export { useAsObservableSource } from './useAsObservableSource' diff --git a/packages/taro-mobx-common/src/propTypes.js b/packages/taro-mobx-common/src/propTypes.js new file mode 100644 index 000000000000..8993ad81d859 --- /dev/null +++ b/packages/taro-mobx-common/src/propTypes.js @@ -0,0 +1,187 @@ +import { isObservableArray, isObservableObject, isObservableMap, untracked } from 'mobx' + +// Copied from React.PropTypes +function createChainableTypeChecker (validate) { + function checkType ( + isRequired, + props, + propName, + componentName, + location, + propFullName, + ...rest + ) { + return untracked(() => { + componentName = componentName || '<>' + propFullName = propFullName || propName + if (props[propName] == null) { + if (isRequired) { + const actual = props[propName] === null ? 'null' : 'undefined' + return new Error( + 'The ' + location + ' `' + propFullName + '` is marked as required ' + + 'in `' + componentName + '`, but its value is `' + actual + '`.' + ) + } + return null + } else { + return validate(props, propName, componentName, location, propFullName, ...rest) + } + }) + } + + const chainedCheckType = checkType.bind(null, false) + chainedCheckType.isRequired = checkType.bind(null, true) + return chainedCheckType +} + +// Copied from React.PropTypes +function isSymbol (propType, propValue) { + // Native Symbol. + if (propType === 'symbol') { + return true + } + + // 19.4.3.5 Symbol.prototype[@@toStringTag] === 'Symbol' + if (propValue['@@toStringTag'] === 'Symbol') { + return true + } + + // Fallback for non-spec compliant Symbols which are polyfilled. + if (typeof Symbol === 'function' && propValue instanceof Symbol) { + return true + } + + return false +} + +// Copied from React.PropTypes +function getPropType (propValue) { + const propType = typeof propValue + if (Array.isArray(propValue)) { + return 'array' + } + if (propValue instanceof RegExp) { + // Old webkits (at least until Android 4.0) return 'function' rather than + // 'object' for typeof a RegExp. We'll normalize this here so that /bla/ + // passes PropTypes.object. + return 'object' + } + if (isSymbol(propType, propValue)) { + return 'symbol' + } + return propType +} + +// This handles more types than `getPropType`. Only used for error messages. +// Copied from React.PropTypes +function getPreciseType (propValue) { + const propType = getPropType(propValue) + if (propType === 'object') { + if (propValue instanceof Date) { + return 'date' + } else if (propValue instanceof RegExp) { + return 'regexp' + } + } + return propType +} + +function createObservableTypeCheckerCreator (allowNativeType, mobxType) { + return createChainableTypeChecker(function ( + props, + propName, + componentName, + location, + propFullName + ) { + return untracked(() => { + if (allowNativeType) { + if (getPropType(props[propName]) === mobxType.toLowerCase()) return null + } + let mobxChecker + switch (mobxType) { + case 'Array': + mobxChecker = isObservableArray + break + case 'Object': + mobxChecker = isObservableObject + break + case 'Map': + mobxChecker = isObservableMap + break + default: + throw new Error(`Unexpected mobxType: ${mobxType}`) + } + const propValue = props[propName] + if (!mobxChecker(propValue)) { + const preciseType = getPreciseType(propValue) + const nativeTypeExpectationMessage = allowNativeType + ? ' or javascript `' + mobxType.toLowerCase() + '`' + : '' + return new Error( + 'Invalid prop `' + propFullName + '` of type `' + preciseType + '` supplied to' + + ' `' + componentName + '`, expected `mobx.Observable' + mobxType + + '`' + nativeTypeExpectationMessage + '.' + ) + } + return null + }) + }) +} + +function createObservableArrayOfTypeChecker (allowNativeType, typeChecker) { + return createChainableTypeChecker(function ( + props, + propName, + componentName, + location, + propFullName, + ...rest + ) { + return untracked(() => { + if (typeof typeChecker !== 'function') { + return new Error( + 'Property `' + propFullName + '` of component `' + componentName + + '` has ' + 'invalid PropType notation.' + ) + } + let error = createObservableTypeCheckerCreator(allowNativeType, 'Array')( + props, + propName, + componentName + ) + if (error instanceof Error) return error + const propValue = props[propName] + for (let i = 0; i < propValue.length; i++) { + error = typeChecker( + propValue, + i, + componentName, + location, + propFullName + '[' + i + ']', + ...rest + ) + if (error instanceof Error) return error + } + return null + }) + }) +} + +const observableArray = createObservableTypeCheckerCreator(false, 'Array') +const observableArrayOf = createObservableArrayOfTypeChecker.bind(null, false) +const observableMap = createObservableTypeCheckerCreator(false, 'Map') +const observableObject = createObservableTypeCheckerCreator(false, 'Object') +const arrayOrObservableArray = createObservableTypeCheckerCreator(true, 'Array') +const arrayOrObservableArrayOf = createObservableArrayOfTypeChecker.bind(null, true) +const objectOrObservableObject = createObservableTypeCheckerCreator(true, 'Object') + +export const PropTypes = { + observableArray, + observableArrayOf, + observableMap, + observableObject, + arrayOrObservableArray, + arrayOrObservableArrayOf, + objectOrObservableObject +} diff --git a/packages/taro-mobx-h5/src/index.js b/packages/taro-mobx-h5/src/index.js index c3f74f23c037..e94225869f74 100644 --- a/packages/taro-mobx-h5/src/index.js +++ b/packages/taro-mobx-h5/src/index.js @@ -1,6 +1,7 @@ import { useState } from '@tarojs/taro-h5' import { + PropTypes, onError, observer, isUsingStaticRendering, @@ -21,6 +22,7 @@ function useAsObservableSource (current) { } export default { + PropTypes, onError, observer, inject, @@ -32,6 +34,7 @@ export default { } export { + PropTypes, onError, observer, inject, diff --git a/packages/taro-mobx-rn/src/index.js b/packages/taro-mobx-rn/src/index.js index 4329afd9107c..9b6272e6336f 100644 --- a/packages/taro-mobx-rn/src/index.js +++ b/packages/taro-mobx-rn/src/index.js @@ -1,6 +1,7 @@ import { useState } from '@tarojs/taro-rn' import { + PropTypes, onError, observer, isUsingStaticRendering, @@ -21,6 +22,7 @@ function useAsObservableSource (current) { } export default { + PropTypes, onError, observer, inject, @@ -32,6 +34,7 @@ export default { } export { + PropTypes, onError, observer, inject, diff --git a/packages/taro-mobx/src/index.js b/packages/taro-mobx/src/index.js index 8ef2ecebac2f..6f716295526d 100644 --- a/packages/taro-mobx/src/index.js +++ b/packages/taro-mobx/src/index.js @@ -1,6 +1,7 @@ import { useState } from '@tarojs/taro' import { + PropTypes, onError, getStore, setStore, @@ -24,6 +25,7 @@ function useAsObservableSource (current) { } export default { + PropTypes, onError, getStore, setStore, @@ -37,6 +39,7 @@ export default { } export { + PropTypes, onError, getStore, setStore, diff --git a/packages/taro-mobx/types/index.d.ts b/packages/taro-mobx/types/index.d.ts index c33017b62794..dc544859fd52 100644 --- a/packages/taro-mobx/types/index.d.ts +++ b/packages/taro-mobx/types/index.d.ts @@ -2,8 +2,6 @@ import Taro from '@tarojs/taro' export type IValueMap = { [key: string]: any }; -export class Provider extends Taro.Component {}; - export function onError(fn: (error: Error) => void); export function isUsingStaticRendering(): boolean; @@ -17,4 +15,16 @@ export function useAsObservableSource(current: TSource): TSource; export function observer(component); export function inject(...stores: string[]); -export function inject(fn: (stores: IValueMap, nextProps: IValueMap) => IValueMap); \ No newline at end of file +export function inject(fn: (stores: IValueMap, nextProps: IValueMap) => IValueMap); + +export class Provider extends Taro.Component {} + +export const PropTypes: { + observableArray: React.Requireable + observableArrayOf: (type: React.Validator) => React.Requireable + observableMap: React.Requireable + observableObject: React.Requireable + arrayOrObservableArray: React.Requireable + arrayOrObservableArrayOf: (type: React.Validator) => React.Requireable + objectOrObservableObject: React.Requireable +} \ No newline at end of file From ee66302f89b2aafbebc228cbe94f183f5243706a Mon Sep 17 00:00:00 2001 From: "Tom.Huang" Date: Wed, 3 Jul 2019 19:57:11 +0800 Subject: [PATCH 6/6] feat(mobx): update docs --- docs/mobx.md | 396 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 245 insertions(+), 151 deletions(-) diff --git a/docs/mobx.md b/docs/mobx.md index 59d03f417612..b20bb40ed003 100644 --- a/docs/mobx.md +++ b/docs/mobx.md @@ -6,9 +6,7 @@ title: 使用 MobX [MobX](https://mobx.js.org/) 为复杂项目中状态管理提供了一种简单高效的机制;Taro 提供了 `@tarojs/mobx` 来让开发人员在使用 MobX 的过程中获得更加良好的开发体验。 -> 下文中示例代码均在 [taro-mobx-sample](https://github.com/nanjingboy/taro-mobx-sample) - -首先请安装 `mobx@4.8.0` 、 `@tarojs/mobx` 、 `@tarojs/mobx-h5` 和 `@tarojs/mobx-rn` +## 安装 ```bash $ yarn add mobx@4.8.0 @tarojs/mobx @tarojs/mobx-h5 @tarojs/mobx-rn @@ -16,168 +14,240 @@ $ yarn add mobx@4.8.0 @tarojs/mobx @tarojs/mobx-h5 @tarojs/mobx-rn $ npm install --save mobx@4.8.0 @tarojs/mobx @tarojs/mobx-h5 @tarojs/mobx-rn ``` -随后可以在项目 `src` 目录下新增一个 `store/counter.js` 文件 +## API + +### onError + +Mobx 异常监听。 ```jsx -// src/store/counter.js -import { observable } from 'mobx' - -const counterStore = observable({ - counter: 0, - counterStore() { - this.counter++ - }, - increment() { - this.counter++ - }, - decrement() { - this.counter-- - }, - incrementAsync() { - setTimeout(() => { - this.counter++ - }, 1000) - } +import { onError } from '@tarojs/mobx' + +onError(error => { + console.log('mobx global error listener:', error) }) +``` + +### isUsingStaticRendering + +> 自 `1.3.6` 开始支持 + +判断是否开启了服务端渲染(该状态为全局状态)。 -export default counterStore +```jsx +import { isUsingStaticRendering } from '@tarojs/mobx' + +if (isUsingStaticRendering()) { + //... +} ``` -接下来在项目入口文件 `app.js` 中使用 `@tarojs/mobx` 中提供的 `Provider` 组件将前面写好的 `store` 接入应用中 +### useStaticRendering + +> 自 `1.3.6` 开始支持 + +服务端渲染状态设置(该状态为全局状态)。 ```jsx -// src/app.js -import Taro, { Component } from '@tarojs/taro' -import { Provider, onError } from '@tarojs/mobx' -import Index from './pages/index' +import { useStaticRendering } from '@tarojs/mobx' -import counterStore from './store/counter' +useStaticRendering(false) +``` -import './app.scss' +### useLocalStore -const store = { - counterStore -} +> 自 `1.3.6` 开始支持 -onError(error => { - console.log('mobx global error listener:', error) -}) +将对象转换为 `observable` 对象,其中 `getter` 会被转换为 `computed` 属性,方法会与 `store` 进行绑定并自动执行 +[mobx transactions](https://mobx.js.org/refguide/action.html),比如: -class App extends Component { +```jsx +import Taro from '@tarojs/taro' +import { View, Text, Button } from '@tarojs/components' +import { useLocalStore, observer } from '@tarojs/mobx' - config = { - pages: [ - 'pages/index/index' - ], - window: { - backgroundTextStyle: 'light', - navigationBarBackgroundColor: '#fff', - navigationBarTitleText: 'WeChat', - navigationBarTextStyle: 'black' +import './index.scss' + +function Index() { + const store = useLocalStore(() => ({ + counter: 0, + increment() { + store.counter++ + }, + decrement() { + store.counter-- + }, + incrementAsync() { + setTimeout(() => store.counter++, 1000) } - } + })) + + const { counter, increment, decrement, incrementAsync } = store; + return ( + + + + + {counter} + + ) +} - componentDidMount () {} +export default observer(Index) +``` - componentDidShow () {} +### useAsObservableSource - componentDidHide () {} +> 自 `1.3.6` 开始支持 - componentDidCatchError () {} +与 `useLocalStore` 的区别是,它将纯(不包含 `getter` 或方法)对象转换为 `observable`,主要使用场景为: - render () { +* 如果对象某个属性的值需经过复杂运算才能获得,可通过该方法进行包装,这样在组件的生命周期中该运算只需要运算一次。 +* 一般情况下 `useLocalStore` 仅用于组件内部,如果 `useLocalStore` 中的对象需要依赖外部传递的属性,那么可通过 + `useAsObservableSource` 将这些属性进行转换,而后在 `useLocalStore` 对象中进行引用,这样在外部属性改变时自动通知 + `useLocalStore` 对象对变化进行响应,比如: + + ```jsx + import Taro from '@tarojs/taro' + import { View, Button, Text } from '@tarojs/components' + import { useAsObservableSource, useLocalStore, observer } from '@tarojs/mobx' + + function Multiplier(props) { + const observableProps = useAsObservableSource(props) + const store = useLocalStore(() => ({ + counter: 1, + get multiplied() { + return observableProps.multiplier * store.counter + }, + increment() { + store.counter += 1 + } + })) + const { multiplier } = observableProps + const { multiplied, counter, increment } = store return ( - - - + + multiplier({multiplier}) * counter({counter}) = {multiplied} + + ) } -} -Taro.render(, document.getElementById('app')) + export default observer(Multiplier) + ``` -``` + 该场景也可直接使用 `useLocalStore` 中的第二种用法来实现: -然后,我们在页面中可通过 `@tarojs/mobx` 提供的 `inject` 以及 `observer` 方法将 `mobx` 与我们的页面进行关联 + ```jsx + import Taro from '@tarojs/taro' + import { View, Button, Text } from '@tarojs/components' + import { useLocalStore, observer } from '@tarojs/mobx' + + function Multiplier(props) { + const store = useLocalStore(source => ({ + counter: 1, + + get multiplier() { + return source.multiplier + }, + + get multiplied() { + return source.multiplier * store.counter + }, + increment() { + store.counter += 1 + } + }), props) + const { multiplied, counter, increment, multiplier } = store + return ( + + multiplier({multiplier}) * counter({counter}) = {multiplied} + + + ) + } -```jsx -// src/pages/index/index.js -import Taro, { Component } from '@tarojs/taro' -import { View, Button, Text } from '@tarojs/components' -import { observer, inject } from '@tarojs/mobx' + export default observer(Multiplier) + ``` -import './index.scss' +### observer -@inject('counterStore') -@observer -class Index extends Component { +将组件设置为监听者,以便在可观察对象的值改变后触发页面的重新渲染。 - config = { - navigationBarTitleText: '首页' - } +注: - componentWillMount () { - console.log('componentWillMount') - } +* 不要在 `JSX` 中对可观察对象进行引用,比如: - /** - * 该方法将在 observable 对象更新时触发 - */ - componentWillReact () { - console.log('componentWillReact') - } + ```jsx + // 错误,在小程序中值改变后将无法触发重新渲染 + const { counterStore } = this.props + return ( + {counterStore.counter} + ) - componentDidMount () { - console.log('componentDidMount') - } + // 正确 + const { counterStore: { counter } } = this.props + return ( + {counter} + ) + ``` - componentWillUnmount () { - console.log('componentWillUnmount') - } + > 这是因为 `@tarojs/mobx` 通过监听组件的 `render`(小程序编译后为 `_createData`)方法来触发更新;在小程序中,`JSX` + > 的代码会被编译到 `wxml` 文件中,此时对可观察对象的引用(比如:`counterStore.counter`)早已脱离了 + > `@tarojs/mobx` 的监控,故此对该属性的更改并不会触发更新操作。 - componentDidShow () { - console.log('componentDidShow') - } +* 如使用 `@observable` 装饰器来定义可观察对象时,请确保该属性已经初始化,比如: - componentDidHide () { - console.log('componentDidHide') - } + ```js + @observable counter // 错误,值改变后将无法触发重新渲染 + @observable counter = 0 // 正确 + ``` - increment = () => { - const { counterStore } = this.props - counterStore.increment() - } +* 如果 `isUsingStaticRendering` 为 `true`,该方法不做任何事情。 - decrement = () => { - const { counterStore } = this.props - counterStore.decrement() - } +### Provider + +全局 `store` 设置,比如: + +```jsx +import Taro, { Component } from '@tarojs/taro' +import { Provider } from '@tarojs/mobx' +import Index from './pages/index' +import counterStore from './store/counter' - incrementAsync = () => { - const { counterStore } = this.props - counterStore.incrementAsync() +const store = { + counterStore +} + +class App extends Component { + config = { + pages: [ + 'pages/index/index' + ], + window: { + backgroundTextStyle: 'light', + navigationBarBackgroundColor: '#fff', + navigationBarTitleText: 'WeChat', + navigationBarTextStyle: 'black' + } } render () { - const { counterStore: { counter } } = this.props return ( - - - - - {counter} - + + + ) } } -export default Index - +Taro.render(, document.getElementById('app')) ``` -上例中 `Provider`、`inject`、 `observer`、`onError` 的使用方式基本上与 [mobx-react](https://github.com/mobxjs/mobx-react) 保持了一致,但也有以下几点需要注意: +注: -* `Provider` 不支持嵌套,即全局只能存在一个 `Provider` +* `Provider` 必须作用于入口文件(即:`src/app.js`),在其他地方使用无效。 +* 不支持嵌套,即全局只能存在一个 `Provider`。 * 在 `mobx-react` 中,可通过以下方式设置 `store`: ```jsx @@ -198,52 +268,76 @@ export default Index ``` -* `inject`、 `observer` 不能在 stateless 组件上使用 -* `observer` 不支持任何参数 -* 按照以下方式使用 `inject` 时,不能省略 `observer` 的显式调用: - ```jsx - @inject((stores, props) => ({ - counterStore: stores.counterStore - })) - @observer //这个不能省略 - ``` +### inject -注意事项: +将 `Provider` 中设置的 `store` 提取到组件的 `props` 中,该 `API` 只适用于`类组件`,比如: -* 在 `Component` 的 `render` 方法中,如果需要使用一个 `observable` 对象(该例中为 `counter`),您需要: +```jsx +import Taro, { Component } from '@tarojs/taro' +import { observer, inject } from '@tarojs/mobx' - ```js - const { counterStore: { counter } } = this.props - return ( - {counter} - ) - ``` +import './index.scss' - 而非: +@inject('counterStore') +@observer +class Index extends Component { + //... +} - ```js - const { counterStore } = this.props - return ( - {counterStore.counter} - ) - ``` +export default Index +``` -* 如果使用 `@observable` 装饰器来定义可观察对象时,请确保该属性已经初始化(这是很多情况下属性值改变,页面没刷新的根源所在),比如: +或 - ```js - @observable counter // 错误 - @observable counter = 0 // 正确 - ``` +```jsx +import Taro, { Component } from '@tarojs/taro' +import { observer, inject } from '@tarojs/mobx' -* 自 `1.2.0-beta.5` 后,`propTypes` 已从 `taro-mobx`、`taro-mobx-h5`、`taro-mobx-rn` 中剥离,如需使用,请单独进行安装: +import './index.scss' + +@inject((stores, props) => ({ + counterStore: stores.counterStore +})) +@observer +class Index extends Component { + //... +} - ```bash - $ yarn add @tarojs/mobx-prop-types - # 或者使用 npm - $ npm install --save @tarojs/mobx-prop-types +export default Index +``` + +注: + +* 无论以何种方式使用 `inject`,其后的 `observer` 均不能省略。 +* 不要在 `inject` 中引用可观察对象,这将导致属性改变后页面不更新,比如: + + ```jsx + // 错误 + @inject((stores, props) => ({ + counter: stores.counterStore.counter + })) + + // 正确 + @inject((stores, props) => ({ + counterStore: stores.counterStore + })) ``` - `propTypes` 使用与 [mobx-react](https://github.com/mobxjs/mobx-react#proptypes) 一致 +### PropTypes + +> 自 `1.3.6` 开始支持 + +`@tarojs/mobx` 提供了以下 `PropTypes` 来验证 Mobx 的结构: + +* observableArray +* observableArrayOf +* observableMap +* observableObject +* arrayOrObservableArray +* arrayOrObservableArrayOf +* objectOrObservableObject + +## 资源 - > 注:自 `1.2.27-beta.0` 后,`@tarojs/mobx-prop-types` 已被移除,请使用 [mobx-react](https://github.com/mobxjs/mobx-react#proptypes) 替代。 +示例:[taro-mobx-sample](https://github.com/nanjingboy/taro-mobx-sample) \ No newline at end of file