Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(mobx): 提供 Mobx Hooks API #3599

Merged
merged 6 commits into from
Jul 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
396 changes: 245 additions & 151 deletions docs/mobx.md

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion packages/taro-cli/src/util/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
]
Expand Down
11 changes: 1 addition & 10 deletions packages/taro-cli/templates/mobx/appjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -16,10 +13,6 @@ import './app.<%= css %>'
// require('nerv-devtools')
// }

const store = {
counterStore
}

class App extends Component {

<%if (locals.typescript) {-%>
Expand Down Expand Up @@ -55,9 +48,7 @@ class App extends Component {
// 请勿修改此函数
render () {
return (
<Provider store={store}>
<Index />
</Provider>
<Index />
)
}
}
Expand Down
8 changes: 0 additions & 8 deletions packages/taro-cli/templates/mobx/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand All @@ -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) {
Expand Down
108 changes: 27 additions & 81 deletions packages/taro-cli/templates/mobx/pagejs
Original file line number Diff line number Diff line change
@@ -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 (
<View>
<Button onClick={increment}>+</Button>
<Button onClick={decrement}>-</Button>
<Button onClick={incrementAsync}>Add Async</Button>
<Text>{counter}</Text>
</View>
)
}

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 (
<View className='<%= pageName %>'>
<Button onClick={this.increment}>+</Button>
<Button onClick={this.decrement}>-</Button>
<Button onClick={this.incrementAsync}>Add Async</Button>
<Text>{counter}</Text>
</View>
)
}
}

export default <%= _.capitalize(pageName) %> <%if (locals.typescript) {%> as ComponentType<%}%>
export default observer(Index)
20 changes: 0 additions & 20 deletions packages/taro-cli/templates/mobx/store/counterjs

This file was deleted.

10 changes: 8 additions & 2 deletions packages/taro-mobx-common/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import { errorsReporter } from './reporter'

export const onError = fn => errorsReporter.on(fn)

export * from './inject'
export { PropTypes } from "./propTypes"

export { observer } from './observer'
export { getStore, setStore } from './store'
export { useLocalStore } from './useLocalStore'
export { useAsObservableSource } from './useAsObservableSource'
export { isUsingStaticRendering, useStaticRendering } from './staticRendering'

export { getStore, setStore } from './store'
export { inject, getInjectName, mapStoreToProps } from './inject'
31 changes: 8 additions & 23 deletions packages/taro-mobx-common/src/inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 */) {
Expand All @@ -62,4 +47,4 @@ export function inject (/* fn(stores, nextProps) or ...storeNames, createStoreIn
return createStoreInjector(grabStoresFn, storeNames.join('-'), componentClass)
}
}
}
}
66 changes: 28 additions & 38 deletions packages/taro-mobx-common/src/observer.js
100755 → 100644
Original file line number Diff line number Diff line change
@@ -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)) ||
'<component>'
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
}
Expand All @@ -65,5 +55,5 @@ export function observer (Component, renderMedthod) {
return result
}

return ObserverComponent
return component
}
Loading