Skip to content

Instantly share code, notes, and snippets.

@xufei
Last active May 2, 2017 11:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xufei/bae6b1ef61403b60d9f2419b19f02815 to your computer and use it in GitHub Desktop.
Save xufei/bae6b1ef61403b60d9f2419b19f02815 to your computer and use it in GitHub Desktop.
const delay = (time) => {
return new Promise(resolve => setTimeout(resolve, time));
};
const serviceFactory = (timeout) => {
return async () => {
await delay(timeout);
return timeout;
};
};
class Simple extends Model {
constructor(config = {
name: 'simple',
state: {
foo: 0,
bar: 0,
counter: 0,
async: 0,
multiSteps: 0,
parallel: [0, 0],
race: 0,
},
}) {
super(config);
}
/**
* 使用put操作修改state
* @param Action 一个Action实例
*/
foo({ payload: foo }) {
this.put({
type: 'foo',
payload: { foo },
});
return foo;
}
/**
* 使用merge简化修改state
* @param Action 一个Action实例
*/
bar({ payload: bar }) {
this.merge({
bar,
});
}
/**
* 使用select操作选取state中的部分数据
* @param Action 一个Action实例
*/
async add({ payload: inc }) {
const { counter } = this.state;
this.put({
type: 'counter',
payload: {
counter: counter + inc,
},
});
}
/**
* 异步的action,在操作之后设置数据
*/
async asyncDemo(action) {
await delay(3000);
this.put(action);
return 'async demo';
}
/**
* 在dispatch之后调用一些操作,dispatch默认也是返回promise的
*/
async asyncDispatchDemo(action) {
const result = await this.foo(action);
this.put({
type: 'asyncDispatch',
payload: {
async: result,
},
});
return result;
}
/**
* 测试组合几个步骤
*/
async multiSteps({ payload: num }) {
const a = await this.add2(num);
const b = await this.add3(a);
return b;
}
// 普通方法,不需要被外部dispatch调用,所以参数不必一定是action
async add2(num) {
await delay(3000);
const result = num + 1;
this.put({
type: 'add2',
payload: {
multiSteps: result,
},
});
return result;
}
// 普通方法,不需要被外部dispatch调用,所以参数不必一定是action
async add3(num) {
await delay(3000);
const result = num + 3;
this.put({
type: 'add3',
payload: {
multiSteps: result,
},
});
return result;
}
/**
* 并行的action,等所有事情做完之后发action
*/
async parallelDemo() {
const service1 = serviceFactory(3000);
const service2 = serviceFactory(2000);
const [a, b] = await Promise.all([
service1(),
service2(),
]);
this.put({
type: 'parallel',
payload: {
parallel: [a, b],
},
});
return [a, b];
}
/**
* 竞争的action,其中一个做完之后就发action
*/
async raceDemo() {
const service = serviceFactory(2000);
const result = await Promise.race([
service(),
delay(3000),
]);
const race = result || 'race';
this.put({
type: 'race',
payload: {
race,
},
});
return result;
}
}
export { Simple };
import React from 'react';
import { connect } from 'react-redux';
const Simple = ({ simple, dispatch }) => {
const style = {
border: '1px solid red',
display: simple.loading ? '' : 'none',
};
const foo = () => {
dispatch({ type: 'simple/foo', payload: 233 });
};
const bar = () => {
dispatch({ type: 'simple/bar', payload: 666 });
};
const add = () => {
dispatch({ type: 'simple/add', payload: 12306 });
};
const asyncDemo = () => {
dispatch({ type: 'simple/asyncDemo', payload: { async: 666 } })
.then((a) => { console.log(a); });
};
const parallelDemo = () => {
dispatch({ type: 'simple/parallelDemo' })
.then((a) => { console.log(a); });
};
const raceDemo = () => {
dispatch({ type: 'simple/raceDemo' })
.then((a) => { console.log(a); });
};
const multiStepDemo = () => {
dispatch({ type: 'simple/multiSteps', payload: 1 })
.then((a) => { console.log(a); });
};
return (
<div>
<button onClick={foo} >foo</button>
<button onClick={bar} >bar</button>
<button onClick={add} >add counter</button>
<button onClick={asyncDemo} >async</button>
<button onClick={multiStepDemo} >multi step</button>
<button onClick={parallelDemo} >parallel</button>
<button onClick={raceDemo} >race</button>
<h2>foo: { simple.foo }</h2>
<h2>bar: { simple.bar }</h2>
<h2>counter: { simple.counter }</h2>
<h2>async: { simple.async }</h2>
<h2>multi step: { simple.multiSteps }</h2>
<h2>parallel: { simple.parallel }</h2>
<h2>race: { simple.race }</h2>
<h2 style={style}>loading</h2>
</div>
);
};
Simple.propTypes = {
};
const mapStateToProps = (state) => {
const { simple } = state;
return {
simple,
};
};
export default connect(mapStateToProps)(Simple);
import { Simple } from './simple'
describe('simple model test', () => {
it('foo should work', () => {
const app = new App()
const simple = new Simple()
app.model('simple', simple)
const dispatch = app.dispatch
expect(simple.state.foo).toEqual(0)
dispatch({ type: 'simple/foo', payload: 1 })
expect(simple.state.foo).toEqual(1)
})
it('bar should work', () => {
const app = new App()
const simple = new Simple()
app.model('simple', simple)
const dispatch = app.dispatch
expect(simple.state.bar).toEqual(0)
dispatch({ type: 'simple/bar', payload: 1 })
expect(simple.state.bar).toEqual(1)
})
it('add should work', () => {
const app = new App()
const simple = new Simple()
app.model('simple', simple)
const dispatch = app.dispatch
expect(simple.state.counter).toEqual(0)
dispatch({ type: 'simple/add', payload: 1 })
expect(simple.state.counter).toEqual(1)
})
it('asyncDemoshould work', async () => {
const app = new App()
const simple = new Simple()
app.model('simple', simple)
const dispatch = app.dispatch
expect(simple.state.counter).toEqual(0)
const result = await dispatch({ type: 'simple/asyncDemo', payload: 1 })
expect(result).toEqual('async demo')
})
it('multiSteps should work', () => {
const app = new App()
const simple = new Simple()
app.model('simple', simple)
const dispatch = app.dispatch
expect(simple.state.multiSteps).toEqual(0)
dispatch({ type: 'simple/multiSteps', payload: 1 })
.then(() => {
expect(simple.state.multiSteps).toEqual(6)
})
})
it('asyncDispatchDemo should work', async () => {
const app = new App()
const simple = new Simple()
app.model('simple', simple)
const dispatch = app.dispatch
expect(simple.state.async).toEqual(0)
const num = await dispatch({ type: 'simple/asyncDispatchDemo', payload: 1 })
expect(simple.state.async).toEqual(num)
})
it('parallelDemo should work', async () => {
const app = new App()
const simple = new Simple()
app.model('simple', simple)
const dispatch = app.dispatch
expect(simple.state.parallel).toEqual([0, 0])
await dispatch({ type: 'simple/parallelDemo' })
expect(simple.state.parallel).toEqual([3000, 2000])
})
it('raceDemo should work', async () => {
const app = new App()
const simple = new Simple()
app.model('simple', simple)
const dispatch = app.dispatch
expect(simple.state.race).toEqual(0)
await dispatch({ type: 'simple/raceDemo' })
expect(simple.state.race).toEqual(2000)
})
})
@xufei
Copy link
Author

xufei commented May 2, 2017

dva简化版

  • 考虑一个应用中支持多个app(多应用集成的场景?),所以让App可实例化
  • 考虑让model也能支持领域模型,所以让model也可以实例化了
  • 视图调用model,还是使用dispatch
  • model内部更新state使用put,put操作的action里面,payload约定必须是对象结构,另外有一个简化的merge操作,可以不需要完整action结构,犹豫要不要公开出来,对跟踪不利
  • 简化版没有call这样的操作,直接用this调用相同model上其他的方法
  • select操作需要考虑,是否可能实现类似xpath或者css选择器那样的
  • takeEvery和takeLatest操作暂时没有实现,正在考虑
  • 现在可以不依赖redux本身了,但是因为自己实现的connect和provider可能在复杂场景下存在性能问题,暂时还是用了redux-react
  • 正在做接入redux-devtool
  • 跨model的调用方式正在考虑,主要有一个问题,如果要支持同一个model的多实例,怎么指定对方model路径,是个问题

反复纠结是否需要提供兼容dva的call这样的操作,这个地方改了好几次……

实现了这些东西的一个基础的应用,用create-react-app创建的,构建大小gzip之后50多k

@fengmk2
Copy link

fengmk2 commented May 2, 2017

代码目录结构约定,也同时给出?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment