metaobject-mvc
v1.2.21
Published
MVC design pattern for ReactJS
Downloads
8
Maintainers
Readme
MVC design pattern for ReactJS
Install
NPM
> npm install --save metaobject-mvc
> npm install --save babel-plugin-transform-decorators-legacy
Edit package.json
...
"babel": {
"presets": [
"react-app"
],
"plugins": [
"transform-decorators-legacy"
]
},
...
Counter Example
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Models from 'metaobject-mvc/Models';
import AppStore from 'mvc/AppStore';
import HistoryStore from 'mvc/HistoryStore';
import App from 'mvc/App';
Models.register(new AppStore('appStore'));
Models.register(new HistoryStore('historyStore'));
ReactDOM.render(<App />, document.getElementById('root'));
App.js
import React from 'react';
import Controller, { connect, disconnect, signal, slot } from 'metaobject-mvc/Controller';
import Models from 'MetaObject-mvc/Models';
@Controller({
models:['appStore','historyStore'],
mount:function (appStore,historyStore) {
connect(this,'increase',appStore,'increase');
connect(this,'decrease',appStore,'decrease');
connect(appStore,'stateChanged',this,'stateChanged');
connect(appStore,'stateChanged',historyStore,'push');
connect(historyStore,'stateChanged',this,'historyChanged');
},
unmount:function (appStore,historyStore) {
disconnect(this,'increase',appStore,'increase');
disconnect(this,'decrease',appStore,'decrease');
disconnect(appStore,'stateChanged',this,'stateChanged');
disconnect(appStore,'stateChanged',historyStore,'push');
disconnect(historyStore,'stateChanged',this,'historyChanged');
}
})
export default class App extends React.Component {
controllerDidMount() {
console.log('controllerDidMount');
}
controllerWillUnmount() {
console.log('controllerWillUnmount');
}
render() {
let {appStore} = this.props;
let {historyStore} = this.props;
let logs = historyStore.records.map((r,i)=>{
return (
<div
key={i}
style={{
borderStyle:'solid',
borderWidth:1
}}
>
{JSON.stringify(r)}
</div>
)}
);
return (
<div
>
<div>{appStore.count}</div>
<button onClick={this.onIncrement(1)}>+1</button>
<button onClick={this.onDecrement(1)}>-1</button>
<button onClick={this.onIncrement(5)}>+5</button>
<button onClick={this.onDecrement(5)}>-5</button>
<button onClick={this.incrementIfOdd}>Increment 1 if odd</button>
<button onClick={this.incrementAsync}>Increment 1 async</button>
<textarea ref={'input'} rows={4} cols={80}
defaultValue={'{"appStore":{"count":5},"historyStore":{"records":[{"0":{"count":0},"1":{"count":1},"when":1533238613508},{"0":{"count":1},"1":{"count":0},"when":1533238614260},{"0":{"count":0},"1":{"count":5},"when":1533238614700}]}}'}>
</textarea>
<button onClick={this.updateStates}>Update states</button>
{logs}
</div>
);
}
@slot
stateChanged(oldState,newState) {
console.log('stateChanged');
}
@slot
historyChanged(oldState,newState) {
console.log('historyChanged',JSON.stringify(Models.getStates()));
this.forceUpdate();
}
@signal
increase(number) {
}
@signal
decrease(number) {
}
onIncrement = (number)=> (e)=>{
this.increase(number);
}
onDecrement = (number)=> (e)=>{
this.decrease(number);
}
incrementIfOdd = (e)=>{
if (this.props.appStore.count % 2 !== 0) {
this.increase(1);
}
}
incrementAsync = (e)=>{
setTimeout(()=>this.increase(1), 1000);
}
updateStates = (e)=>{
try {
Models.setStates(JSON.parse(this.refs.input.value));
} catch(e) {
} finally {
this.refs.input.value = '';
}
}
}
or if you use only one model, you can
@Controller({
model:'appStore',
mount:function (appStore) {
...
},
unmount:function (appStore) {
...
},
...
})
or if you need initializing by props, you can
ReactDOM.render(<App appStore={'appStore'} historyStore={'historyStore'}/>, document.getElementById('root'));
...
@Controller((props)=>({
models:[props.appStore,props.historyStore],
mount:function (appStore,historyStore) {
...
},
unmount:function (appStore,historyStore) {
...
},
...
}))
AppStore.js
import Model, { signal, slot } from 'metaobject-mvc/Model';
export default class AppStore extends Model {
constructor(name) {
super(name);
this.state({
count: 0,
});
}
@signal
@slot
increase(number) {
let count = this.state().count;
this.state({
count: count+number,
});
}
@signal
@slot
decrease(number) {
let count = this.state().count;
this.state({
count: Math.max(0,count-number),
});
}
}
HistoryStore.js
import Model, { slot } from 'metaobject-mvc/Model';
export default class HistoryStore extends Model {
constructor(name) {
super(name);
this.state({
records:[],
});
}
@slot
push(...record) {
let records = this.state().records;
this.state({
records: [...records,{...record,when:Date.now()}],
});
}
}