Skip to content

Instantly share code, notes, and snippets.

@lijunle
Last active July 14, 2017 15:33
Show Gist options
  • Save lijunle/6b0cc2538b66c36250f42f32baec2e73 to your computer and use it in GitHub Desktop.
Save lijunle/6b0cc2538b66c36250f42f32baec2e73 to your computer and use it in GitHub Desktop.
/** @file App.tsx */
class App extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
mount: true
};
}
render() {
return (
<div>
{
this.state.mount && (
<MVC
model={{ count: 0 }}
view={MyView}
controller={MyController}
/>
)
}
<button onClick={() => this.setState({ mount: true })}>
Mount
</button>
<button onClick={() => this.setState({ mount: false })}>
Unmount
</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById(('app')))
/** @file BaseCollection.ts */
class BaseController<T> {
protected setState: (callback: (prevState: T) => T) => void;
constructor(setState) {
this.setState = setState;
}
}
/** @file MVC.ts */
interface IMVCProps<TModel, TView, TController> {
model: TModel;
view: TView;
controller: TController;
}
class MVC<TModel, TView, TController>
extends React.PureComponent<IMVCProps<TModel, TView, TController>, TModel> {
private mounted = false;
constructor(props) {
super(props);
this.state = props.model;
const setState = this.setState;
this.setState = (prevState) => {
if (this.mounted) {
setState.call(this, prevState);
}
}
this.controller = new (props.controller)(this.setState);
Object.getOwnPropertyNames(Object.getPrototypeOf(this.controller)).forEach((key) => {
if (key === 'constructor') { return; }
this.controller[key] = this.controller[key].bind(this.controller);
});
}
componentDidMount() {
this.mounted = true;
}
componentWillUnmount() {
this.mounted = false;
}
render() {
return React.createElement(this.props.view, {
controller: this.controller,
...this.state
})
}
}
/** @file MyController.ts */
class MyController extends BaseController<MyModel> {
public inc(step: number) {
return () => this.setState(state => ({ count: state.count + step }));
}
public dec() {
this.setState(state => ({ count: state.count - 1 }));
}
public deferInc() {
setTimeout(() => {
this.setState(state => ({ count: state.count + 10 }))
}, 1000)
}
public deferDec() {
setTimeout(() => {
this.setState(state => ({ count: state.count - 10 }))
}, 3000)
}
}
/** @file MyModel.ts */
interface MyModel {
count: number;
}
/** @file MyView.tsx */
const MyView = ({ controller, count }: MyModel & { controller: MyController }) => {
return (
<div>
<button onClick={controller.inc(1)}>Inc</button>
<button onClick={controller.deferInc}>Defer Inc</button>
<div>{count}</div>
<button onClick={controller.dec}>Dec</button>
<button onClick={controller.deferDec}>Defer Dec</button>
</div>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment