omis - lifecycle

* receiveProps `return false` will prevent update action
* Add unit testing
* Build & publish
This commit is contained in:
dntzhang 2019-07-22 15:38:20 +08:00
parent 8685bc0e61
commit 3f7eea04e8
14 changed files with 265 additions and 51 deletions

View File

@ -1,6 +1,6 @@
{
"props": {
"cname": 42,
"cname": 43,
"props": {
"$_dirty": "__d",
"$_disable": "__x",
@ -41,7 +41,8 @@
"$_customStyleElement": "N",
"$_customStyleContent": "O",
"$__hasChildren": "P",
"$__prevProps": "Q"
"$__prevProps": "Q",
"$__needUpdate_": "R"
}
},
"vars": {

View File

@ -1,5 +1,5 @@
/**
* omis v0.3.0 http://omijs.org
* omis v0.4.0 http://omijs.org
* Omi === Preact + Scoped CSS + Store System + Native Support in 3kb javascript.
* By dntzhang https://github.com/dntzhang
* Github: https://github.com/Tencent/omis
@ -487,7 +487,8 @@
addStyleToHead(c.props.css, '_ds' + c.elementId);
}
if (options.afterMount) options.afterMount(c);
if (c.componentDidMount) c.componentDidMount();
//if (c.componentDidMount) c.componentDidMount();
if (c.store.installed) c.store.installed();
}
}
@ -851,9 +852,15 @@
if (typeof component.constructor.getDerivedStateFromProps === 'undefined') {
if (!component.base || mountAll) {
if (component.componentWillMount) component.componentWillMount();
} else if (component.componentWillReceiveProps) {
component.componentWillReceiveProps(props, context);
//if (component.componentWillMount) component.componentWillMount();
if (component.store.install) component.store.install();
} else {
// if (component.componentWillReceiveProps) {
// component.componentWillReceiveProps(props, context);
// }
if (component.store.receiveProps) {
component.__needUpdate_ = component.store.receiveProps(props, context);
}
}
}
@ -916,11 +923,17 @@
component.props = previousProps;
component.state = previousState;
component.context = previousContext;
if (renderMode !== 2 && component.shouldComponentUpdate && component.shouldComponentUpdate(props, state, context) === false) {
if (component.__needUpdate_ !== false) {
skip = false;
if (component.store.beforeUpdate) {
component.store.beforeUpdate(props, state, context);
}
} else {
skip = true;
} else if (component.componentWillUpdate) {
component.componentWillUpdate(props, state, context);
}
delete component.__needUpdate_;
component.props = props;
component.state = state;
component.context = context;
@ -931,6 +944,9 @@
if (!skip) {
options.runTimeComponent = component;
if (component.store.beforeRender) {
component.store.beforeRender();
}
rendered = component.render(props, state, context);
options.runTimeComponent = null;
@ -1017,8 +1033,11 @@
// Note: disabled as it causes duplicate hooks, see https://github.com/developit/preact/issues/750
// flushMounts();
if (component.componentDidUpdate) {
component.componentDidUpdate(previousProps, previousState, snapshot);
// if (component.componentDidUpdate) {
// component.componentDidUpdate(previousProps, previousState, snapshot);
// }
if (component.store.updated) {
component.store.updated(previousProps, previousState, snapshot);
}
if (options.afterUpdate) options.afterUpdate(component);
}
@ -1087,8 +1106,8 @@
component._disable = true;
if (component.componentWillUnmount) component.componentWillUnmount();
//if (component.componentWillUnmount) component.componentWillUnmount();
if (component.store.uninstall) component.store.uninstall();
component.base = null;
// recursively tear down & recollect high-order component children:
@ -1135,7 +1154,7 @@
* @type {object}
*/
this.context = context;
this.store = {};
/**
* @public
* @type {object}

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
/**
* omis v0.3.0 http://omijs.org
* omis v0.4.0 http://omijs.org
* Omi === Preact + Scoped CSS + Store System + Native Support in 3kb javascript.
* By dntzhang https://github.com/dntzhang
* Github: https://github.com/Tencent/omis
@ -484,7 +484,8 @@ function flushMounts() {
addStyleToHead(c.props.css, '_ds' + c.elementId);
}
if (options.afterMount) options.afterMount(c);
if (c.componentDidMount) c.componentDidMount();
//if (c.componentDidMount) c.componentDidMount();
if (c.store.installed) c.store.installed();
}
}
@ -848,9 +849,15 @@ function setComponentProps(component, props, renderMode, context, mountAll) {
if (typeof component.constructor.getDerivedStateFromProps === 'undefined') {
if (!component.base || mountAll) {
if (component.componentWillMount) component.componentWillMount();
} else if (component.componentWillReceiveProps) {
component.componentWillReceiveProps(props, context);
//if (component.componentWillMount) component.componentWillMount();
if (component.store.install) component.store.install();
} else {
// if (component.componentWillReceiveProps) {
// component.componentWillReceiveProps(props, context);
// }
if (component.store.receiveProps) {
component.__needUpdate_ = component.store.receiveProps(props, context);
}
}
}
@ -913,11 +920,17 @@ function renderComponent(component, renderMode, mountAll, isChild) {
component.props = previousProps;
component.state = previousState;
component.context = previousContext;
if (renderMode !== 2 && component.shouldComponentUpdate && component.shouldComponentUpdate(props, state, context) === false) {
if (component.__needUpdate_ !== false) {
skip = false;
if (component.store.beforeUpdate) {
component.store.beforeUpdate(props, state, context);
}
} else {
skip = true;
} else if (component.componentWillUpdate) {
component.componentWillUpdate(props, state, context);
}
delete component.__needUpdate_;
component.props = props;
component.state = state;
component.context = context;
@ -928,6 +941,9 @@ function renderComponent(component, renderMode, mountAll, isChild) {
if (!skip) {
options.runTimeComponent = component;
if (component.store.beforeRender) {
component.store.beforeRender();
}
rendered = component.render(props, state, context);
options.runTimeComponent = null;
@ -1014,8 +1030,11 @@ function renderComponent(component, renderMode, mountAll, isChild) {
// Note: disabled as it causes duplicate hooks, see https://github.com/developit/preact/issues/750
// flushMounts();
if (component.componentDidUpdate) {
component.componentDidUpdate(previousProps, previousState, snapshot);
// if (component.componentDidUpdate) {
// component.componentDidUpdate(previousProps, previousState, snapshot);
// }
if (component.store.updated) {
component.store.updated(previousProps, previousState, snapshot);
}
if (options.afterUpdate) options.afterUpdate(component);
}
@ -1084,8 +1103,8 @@ function unmountComponent(component) {
component._disable = true;
if (component.componentWillUnmount) component.componentWillUnmount();
//if (component.componentWillUnmount) component.componentWillUnmount();
if (component.store.uninstall) component.store.uninstall();
component.base = null;
// recursively tear down & recollect high-order component children:
@ -1132,7 +1151,7 @@ function Component(props, context) {
* @type {object}
*/
this.context = context;
this.store = {};
/**
* @public
* @type {object}

File diff suppressed because one or more lines are too long

View File

@ -146,7 +146,7 @@
if (c.constructor.css) addStyleToHead(c.constructor.css, getCtorName(c.constructor));
if (c.props.css) addStyleToHead(c.props.css, '_ds' + c.elementId);
if (options.afterMount) options.afterMount(c);
if (c.componentDidMount) c.componentDidMount();
if (c.store.installed) c.store.installed();
}
}
function diff(dom, vnode, context, mountAll, parent, componentRoot, store) {
@ -288,8 +288,8 @@
delete props.ref;
delete props.key;
if (void 0 === component.constructor.getDerivedStateFromProps) if (!component.base || mountAll) {
if (component.componentWillMount) component.componentWillMount();
} else if (component.componentWillReceiveProps) component.componentWillReceiveProps(props, context);
if (component.store.install) component.store.install();
} else if (component.store.receiveProps) component.R = component.store.receiveProps(props, context);
if (context && context !== component.context) {
if (!component.__c) component.__c = component.context;
component.context = context;
@ -312,7 +312,11 @@
component.props = previousProps;
component.state = previousState;
component.context = previousContext;
if (2 !== renderMode && component.shouldComponentUpdate && !1 === component.shouldComponentUpdate(props, state, context)) skip = !0; else if (component.componentWillUpdate) component.componentWillUpdate(props, state, context);
if (!1 !== component.R) {
skip = !1;
if (component.store.beforeUpdate) component.store.beforeUpdate(props, state, context);
} else skip = !0;
delete component.R;
component.props = props;
component.state = state;
component.context = context;
@ -321,6 +325,7 @@
component.__d = !1;
if (!skip) {
options.runTimeComponent = component;
if (component.store.beforeRender) component.store.beforeRender();
rendered = component.render(props, state, context);
options.runTimeComponent = null;
if (component.getChildContext) context = extend(extend({}, context), component.getChildContext());
@ -367,7 +372,7 @@
}
}
if (!isUpdate || mountAll) mounts.push(component); else if (!skip) {
if (component.componentDidUpdate) component.componentDidUpdate(previousProps, previousState, snapshot);
if (component.store.updated) component.store.updated(previousProps, previousState, snapshot);
if (options.afterUpdate) options.afterUpdate(component);
}
while (component.__h.length) component.__h.pop().call(component);
@ -403,7 +408,7 @@
if (options.beforeUnmount) options.beforeUnmount(component);
var base = component.base;
component.__x = !0;
if (component.componentWillUnmount) component.componentWillUnmount();
if (component.store.uninstall) component.store.uninstall();
component.base = null;
var inner = component._component;
if (inner) unmountComponent(inner); else if (base) {
@ -419,6 +424,7 @@
this.__d = !0;
this.elementId = id++;
this.context = context;
this.store = {};
this.props = props;
this.state = this.state || {};
this.__h = [];

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "omis",
"version": "0.3.0",
"version": "0.4.0",
"description": "Functional Component with store, scoped css and easy hyperscript.",
"main": "dist/omis.js",
"jsnext:main": "dist/omis.esm.js",

View File

@ -34,7 +34,7 @@ export function setComponentProps(component, props, renderMode, context, mountAl
// component.componentWillReceiveProps(props, context);
// }
if (component.store.receiveProps) {
component.store.receiveProps(props, context)
component.__needUpdate_ = component.store.receiveProps(props, context)
}
}
}
@ -99,19 +99,17 @@ export function renderComponent(component, renderMode, mountAll, isChild) {
component.props = previousProps;
component.state = previousState;
component.context = previousContext;
if (renderMode!==FORCE_RENDER
&& component.shouldComponentUpdate
&& component.shouldComponentUpdate(props, state, context) === false) {
skip = true;
}
else {
// if (component.componentWillUpdate) {
// component.componentWillUpdate(props, state, context);
// }
if(component.store.beforeUpdate){
if (component.__needUpdate_ !== false) {
skip = false
if (component.store.beforeUpdate) {
component.store.beforeUpdate(props, state, context)
}
} else {
skip = true
}
delete component.__needUpdate_
component.props = props;
component.state = state;
component.context = context;

View File

@ -105,4 +105,89 @@ describe('lifecycle', () => {
expect(arr).to.deep.equal(['install', 'beforeRender', 'installed', 'beforeUpdate', 'beforeRender', 'updated', 'receiveProps', 'beforeUpdate', 'beforeRender', 'updated', 'uninstall'])
})
it('receiveProps return false test', () => {
const arr = []
const Counter = (props, store) => {
return (
<div>
<button class='simple-test-a' onClick={store.sub}>-</button>
<span>{store.count}</span>
<button onClick={store.add}>+</button>
</div>
)
}
Counter.store = _ => {
return {
count: 1,
add(e) {
this.count++
this.update()
_.props.onChange(this.count)
},
sub() {
this.count--
this.update()
_.props.onChange(this.count)
},
install() {
arr.push('install')
},
installed() {
arr.push('installed')
},
uninstall() {
arr.push('uninstall')
},
beforeUpdate() {
arr.push('beforeUpdate')
},
updated() {
arr.push('updated')
},
beforeRender() {
arr.push('beforeRender')
},
receiveProps() {
arr.push('receiveProps')
return false
}
}
}
const App = (props, store) => {
return (
<div>
<div class='simple-test-b' onClick={store.toggle}>Count from child event: {store.count}</div>
{store.show && <Counter onChange={store.changeHandle}></Counter>}
</div>
)
}
App.store = _ => {
class Store {
count = null
show = true
changeHandle = (count) => {
this.count = count
this.update()
}
toggle = () => {
this.show = !this.show
this.update()
}
}
return new Store
}
render(<App />, scratch)
document.querySelector('.simple-test-a').click()
document.querySelector('.simple-test-b').click()
expect(arr).to.deep.equal(['install', 'beforeRender', 'installed', 'beforeUpdate', 'beforeRender', 'updated', 'receiveProps', 'uninstall'])
})
})

View File

@ -0,0 +1,43 @@
## Lifecycle
| Lifecycle method | When it gets called |
| ---------------- | -------------------------------------------- |
| `install` | before the component gets mounted to the DOM |
| `installed` | after the component gets mounted to the DOM |
| `uninstall` | prior to removal from the DOM |
| `beforeUpdate` | before update |
| `updated` | after update |
| `beforeRender` | before `render()` |
| `receiveProps` | parent element re-render will trigger it, `return false` will prevent update action |
For example:
```js
import { render, WeElement, define } from 'omi'
define('my-timer', class extends WeElement {
static observe = true
data = {
seconds: 0
}
tick() {
this.data.seconds++
}
install() {
this.interval = setInterval(() => this.tick(), 1000)
}
uninstall() {
clearInterval(this.interval)
}
render() {
return <div>Seconds: {this.data.seconds}</div>
}
})
render(<my-timer />, 'body')
```

View File

@ -0,0 +1,43 @@
### 生命周期
| 方法名 | 执行时机 |
| ---------------- | -------------------------------------------- |
| `install` | 准备插入到文档 |
| `installed` | 插入到文档之后 |
| `uninstall` | 从文档中移除 |
| `beforeUpdate` | update 之前 |
| `updated` | update 之后 |
| `beforeRender` | `render()` 之前 |
| `receiveProps` | 父元素重新渲染触发,返回 false 可阻止更新 |
举个例子:
```js
import { render, WeElement, define } from 'omi'
define('my-timer', class extends WeElement {
data = {
seconds: 0
}
tick() {
this.data.seconds++
this.update()
}
install() {
this.interval = setInterval(() => this.tick(), 1000)
}
uninstall() {
clearInterval(this.interval)
}
render() {
return <div>Seconds: {this.data.seconds}</div>
}
})
render(<my-timer />, 'body')
```