-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* ✨ 💥 adding `prepareStore` to setup `redux-batched-actions` and `redux-saga` and some built-in reducers to make dev easier * revamped middleware
- Loading branch information
Showing
14 changed files
with
523 additions
and
391 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import test from 'ava'; | ||
import createSagaMiddleware, { SagaIterator } from 'redux-saga'; | ||
import { put, call } from 'redux-saga/effects'; | ||
import { takeEvery, put, call } from 'redux-saga/effects'; | ||
import { | ||
createAction, | ||
createReducerMap, | ||
|
@@ -13,6 +13,7 @@ import sagaCreator from 'redux-saga-creator'; | |
import { urlParser, queryCtx } from './middleware'; | ||
import { FetchCtx } from './fetch'; | ||
import { createApi } from './api'; | ||
import { setupStore } from './util'; | ||
|
||
interface User { | ||
id: string; | ||
|
@@ -23,17 +24,6 @@ interface User { | |
const mockUser: User = { id: '1', name: 'test', email: '[email protected]' }; | ||
const mockUser2: User = { id: '2', name: 'two', email: '[email protected]' }; | ||
|
||
function setupStore( | ||
saga: any, | ||
reducers: any = { users: (state: any = {}) => state }, | ||
) { | ||
const sagaMiddleware = createSagaMiddleware(); | ||
const reducer = combineReducers(reducers); | ||
const store: any = createStore(reducer, applyMiddleware(sagaMiddleware)); | ||
sagaMiddleware.run(saga); | ||
return store; | ||
} | ||
|
||
test('createApi - POST', (t) => { | ||
t.plan(1); | ||
const name = 'users'; | ||
|
@@ -138,6 +128,10 @@ test('run() from a normal saga', (t) => { | |
t.assert(acc === 'ab'); | ||
} | ||
|
||
const store = setupStore(sagaCreator({ api: api.saga(), action: onAction })); | ||
function* watchAction() { | ||
yield takeEvery(`${action2}`, onAction); | ||
} | ||
|
||
const store = setupStore({ api: api.saga(), watchAction }); | ||
store.dispatch(action2()); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
export { BATCH, batchActions } from 'redux-batched-actions'; | ||
export * from './pipe'; | ||
export * from './api'; | ||
export * from './types'; | ||
export * from './fetch'; | ||
export * from './middleware'; | ||
export * from './constants'; | ||
export * from './store'; | ||
export * from './slice'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,12 +7,18 @@ import { | |
createTable, | ||
createLoaderTable, | ||
} from 'robodux'; | ||
import { createStore, combineReducers, applyMiddleware } from 'redux'; | ||
|
||
import { Next } from './types'; | ||
import { createApi } from './api'; | ||
import { urlParser, loadingTracker, queryCtx } from './middleware'; | ||
import { | ||
urlParser, | ||
queryCtx, | ||
requestParser, | ||
requestMonitor, | ||
} from './middleware'; | ||
import { FetchCtx } from './fetch'; | ||
import { setupStore } from './util'; | ||
import { DATA_NAME, LOADERS_NAME, createQueryState } from './slice'; | ||
|
||
interface User { | ||
id: string; | ||
|
@@ -23,14 +29,6 @@ interface User { | |
const mockUser: User = { id: '1', name: 'test', email: '[email protected]' }; | ||
const mockUser2: User = { id: '2', name: 'two', email: '[email protected]' }; | ||
|
||
function setupStore(saga: any, reducers: any) { | ||
const sagaMiddleware = createSagaMiddleware(); | ||
const reducer = combineReducers(reducers); | ||
const store: any = createStore(reducer, applyMiddleware(sagaMiddleware)); | ||
sagaMiddleware.run(saga); | ||
return store; | ||
} | ||
|
||
function* latest(action: string, saga: any, ...args: any[]) { | ||
yield takeLatest(`${action}`, saga, ...args); | ||
} | ||
|
@@ -92,30 +90,23 @@ test('middleware - basic', (t) => { | |
const store = setupStore(query.saga(), reducers); | ||
store.dispatch(fetchUsers()); | ||
t.deepEqual(store.getState(), { | ||
...createQueryState(), | ||
users: { [mockUser.id]: mockUser }, | ||
}); | ||
store.dispatch(fetchUser({ id: '2' })); | ||
t.deepEqual(store.getState(), { | ||
...createQueryState(), | ||
users: { [mockUser.id]: mockUser, [mockUser2.id]: mockUser2 }, | ||
}); | ||
}); | ||
|
||
test('middleware - with loader', (t) => { | ||
const users = createTable<User>({ name: 'users' }); | ||
const loaders = createLoaderTable({ name: 'loaders' }); | ||
|
||
const api = createApi<FetchCtx>(); | ||
api.use(function* (ctx, next) { | ||
yield next(); | ||
for (let i = 0; i < ctx.actions.length; i += 1) { | ||
const action = ctx.actions[i]; | ||
yield put(action); | ||
} | ||
}); | ||
api.use(loadingTracker(loaders)); | ||
api.use(requestMonitor()); | ||
api.use(api.routes()); | ||
api.use(queryCtx); | ||
api.use(urlParser); | ||
api.use(requestParser()); | ||
api.use(function* fetchApi(ctx, next) { | ||
ctx.response = { | ||
status: 200, | ||
|
@@ -142,13 +133,13 @@ test('middleware - with loader', (t) => { | |
}, | ||
); | ||
|
||
const reducers = createReducerMap(loaders, users); | ||
const reducers = createReducerMap(users); | ||
const store = setupStore(api.saga(), reducers); | ||
|
||
store.dispatch(fetchUsers()); | ||
t.like(store.getState(), { | ||
[users.name]: { [mockUser.id]: mockUser }, | ||
[loaders.name]: { | ||
[LOADERS_NAME]: { | ||
'/users': { | ||
status: 'success', | ||
}, | ||
|
@@ -200,3 +191,91 @@ test('middleware - with POST', (t) => { | |
const store = setupStore(query.saga(), reducers); | ||
store.dispatch(createUser({ email: mockUser.email })); | ||
}); | ||
|
||
test('overriding default loader behavior', (t) => { | ||
const users = createTable<User>({ name: 'users' }); | ||
|
||
const api = createApi<FetchCtx>(); | ||
api.use(requestMonitor()); | ||
api.use(api.routes()); | ||
api.use(requestParser()); | ||
|
||
api.use(function* fetchApi(ctx, next) { | ||
ctx.response = { | ||
status: 200, | ||
ok: true, | ||
data: { | ||
users: [mockUser], | ||
}, | ||
}; | ||
yield next(); | ||
}); | ||
|
||
const fetchUsers = api.create( | ||
`/users`, | ||
function* processUsers(ctx: FetchCtx<{ users: User[] }>, next) { | ||
const id = ctx.name; | ||
yield next(); | ||
if (!ctx.response.ok) { | ||
ctx.loader.error = { id, message: 'boo' }; | ||
return; | ||
} | ||
const { data } = ctx.response; | ||
const curUsers = data.users.reduce<MapEntity<User>>((acc, u) => { | ||
acc[u.id] = u; | ||
return acc; | ||
}, {}); | ||
|
||
ctx.loader.success = { id, message: 'yes', meta: { wow: true } }; | ||
ctx.actions.push(users.actions.add(curUsers)); | ||
}, | ||
); | ||
|
||
const reducers = createReducerMap(users); | ||
const store = setupStore(api.saga(), reducers); | ||
|
||
store.dispatch(fetchUsers()); | ||
t.like(store.getState(), { | ||
[users.name]: { [mockUser.id]: mockUser }, | ||
[LOADERS_NAME]: { | ||
[`${fetchUsers}`]: { | ||
status: 'success', | ||
message: 'yes', | ||
meta: { wow: true }, | ||
}, | ||
}, | ||
}); | ||
}); | ||
|
||
test('quickSave', (t) => { | ||
const api = createApi<FetchCtx>(); | ||
api.use(requestMonitor()); | ||
api.use(api.routes()); | ||
api.use(requestParser()); | ||
api.use(function* fetchApi(ctx, next) { | ||
ctx.response = { | ||
status: 200, | ||
ok: true, | ||
data: { | ||
users: [mockUser], | ||
}, | ||
}; | ||
yield next(); | ||
}); | ||
|
||
const fetchUsers = api.get('/users', api.request({ simpleCache: true })); | ||
const store = setupStore(api.saga()); | ||
|
||
const action = fetchUsers(); | ||
store.dispatch(action); | ||
t.like(store.getState(), { | ||
[DATA_NAME]: { | ||
[JSON.stringify(action)]: { users: [mockUser] }, | ||
}, | ||
[LOADERS_NAME]: { | ||
[`${fetchUsers}`]: { | ||
status: 'success', | ||
}, | ||
}, | ||
}); | ||
}); |
Oops, something went wrong.