Skip to content

Commit

Permalink
Merge pull request #25 from Bloomca/feature/add-get-data-function-to-…
Browse files Browse the repository at this point in the history
…sync-tile-action

Feature/add get data function to sync tile action
  • Loading branch information
Bloomca authored Nov 17, 2017
2 parents 227b6c0 + 022b37c commit 1122d42
Show file tree
Hide file tree
Showing 19 changed files with 127 additions and 66 deletions.
24 changes: 13 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,17 +135,17 @@ const photos = createTile({
// and also available under actions and selectors as:
// actions.tiles.api.photos
type: ['api', 'photos'],


// params is an object with which we dispatch the action
// you can pass only one parameter, so keep it as an object
// with different properties
//
// all other properties are from your middleware
// fn expects promise out of this function
fn: ({ params, api }) => api.get('/photos', params),


// to nest data:
// { 5:
// 10: {
Expand Down Expand Up @@ -176,8 +176,8 @@ import { createSyncTile } from 'redux-tiles';

const notifications = createSyncTile({
type: ['notifications'],


// all parameters are the same as in async tile
fn: ({ params, dispatch, actions }) => {
// we can dispatch async actions – but we can't wait
Expand All @@ -195,8 +195,10 @@ const notifications = createSyncTile({
// they have exactly the same signature and dispatch returned data
// to the tile
fns: {
add: ({ params, selectors, getState}) => {
const currentData = selectors.notifications(getState(), params);
add: ({ params, getData, selectors, getState }) => {
// same as:
// const currentData = selectors.notifications(getState(), params);
const currentData = getData();

return {
...currentData,
Expand All @@ -215,7 +217,7 @@ const notifications = createSyncTile({
data: []
},
},

// nesting works the same way
nesting: ({ type }) => [type],
});
Expand Down Expand Up @@ -244,10 +246,10 @@ Or with `Object.assign`, which will make it even less readable. This is a pretty
```javascript
const infoTile = createTile({
type: ['info', 'storage'],

// params here and in nesting are the same object
fn: ({ params: { quantity, id }, api }) => api.get('/storage', { quantity, id }),

// in the state they will be kept with the following structure:
// {
// someId: {
Expand Down
13 changes: 8 additions & 5 deletions docs/api/createSyncTile.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const chooseParams = createSyncTile({
// alternatively, if you perform some actions on existing data,
// it might be useful to write more declarative actions
// they have exactly the same signature and dispatch returned data
// to the tile; they will be available under
// to the tile; they will be available under
// actions.ui.params.add and actions.ui.params.remove
fns: {
add: ({ params, selectors, getState}) => {
Expand All @@ -66,8 +66,8 @@ const chooseParams = createSyncTile({

## Example

Let's create a tile for todoMVC application. We have to perform a lot of
listing list of all notifications which were triggered. We won't remove them here for the sake of simplicity, but we can easily add it with filtering.
Let's create a tile for todoMVC application. We have to perform a lot of
listing list of all notifications which were triggered. We won't remove them here for the sake of simplicity, but we can easily add it with filtering.

```javascript
import { createSyncTile } from 'redux-tiles';
Expand All @@ -81,8 +81,11 @@ export const todosTile = createSyncTile({
fns: {
// this function will be available under
// actions.todos.list.add
add: ({ params, selectors, getState }) => {
const list = selectors.todos.list(getState());
add: ({ params, getData }) => {
// getData is a special function which returns current value
// it is equivalent for the following:
// const list = selectors.todos.list(getState());
const list = getData();
const newItem = {
...params,
completed: false,
Expand Down
2 changes: 1 addition & 1 deletion examples/calculator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"license": "MIT",
"dependencies": {
"redux": "^3.6.0",
"redux-tiles": "^0.8.0"
"redux-tiles": "^0.9.0"
},
"devDependencies": {
"babel-plugin-transform-async-to-generator": "^6.24.1",
Expand Down
6 changes: 3 additions & 3 deletions examples/calculator/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2281,9 +2281,9 @@ rechoir@^0.6.2:
dependencies:
resolve "^1.1.6"

redux-tiles@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/redux-tiles/-/redux-tiles-0.8.0.tgz#7e3287a6e0335cd697be7616f79c7b320c1e4536"
redux-tiles@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/redux-tiles/-/redux-tiles-0.9.0.tgz#699e15489b0b0a31ecc9ffff8a28d0aebab47762"
dependencies:
redux "^3.6.0"

Expand Down
2 changes: 1 addition & 1 deletion examples/github-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"axios": "^0.16.2",
"firebase": "^4.1.1",
"redux": "^3.6.0",
"redux-tiles": "^0.8.0"
"redux-tiles": "^0.9.0"
},
"devDependencies": {
"babel-plugin-transform-async-to-generator": "^6.24.1",
Expand Down
6 changes: 3 additions & 3 deletions examples/github-api/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2381,9 +2381,9 @@ rechoir@^0.6.2:
dependencies:
resolve "^1.1.6"

redux-tiles@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/redux-tiles/-/redux-tiles-0.8.0.tgz#7e3287a6e0335cd697be7616f79c7b320c1e4536"
redux-tiles@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/redux-tiles/-/redux-tiles-0.9.0.tgz#699e15489b0b0a31ecc9ffff8a28d0aebab47762"
dependencies:
redux "^3.6.0"

Expand Down
9 changes: 2 additions & 7 deletions examples/hacker-news-api/hn-tiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,12 @@ export const itemsByPageTile = createTile({
fn: async ({ params: { type = 'topstories', pageNumber = 0, pageSize = 30 }, selectors, getState, actions, dispatch }) => {
// we can always fetch stories, they are cached, so if this type
// was already fetched, there will be no new request
const aaa = await dispatch(actions.hn_api.stories({ type }));
console.log(typeof aaa);
const data = aaa.data;
// const { data } = selectors.hn_api.stories(getState(), { type });
// console.log(aaa.data === data, 'comparing..', data && data.length);
const { data } = await dispatch(actions.hn_api.stories({ type }));
const offset = pageNumber * pageSize;
const end = offset + pageSize;
const ids = data.slice(offset, end);
await dispatch(actions.hn_api.items({ ids }));
return ids.map(id => {
// console.log(selectors.hn_api.item(getState(), { id }));
return selectors.hn_api.item(getState(), { id }).data
});
},
Expand All @@ -65,4 +60,4 @@ export default [
itemsTile,
itemsByPageTile,
userTile,
];
];
2 changes: 1 addition & 1 deletion examples/hacker-news-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"dependencies": {
"firebase": "^4.1.1",
"redux": "^3.6.0",
"redux-tiles": "^0.8.0"
"redux-tiles": "^0.9.0"
},
"devDependencies": {
"babel-plugin-transform-async-to-generator": "^6.24.1",
Expand Down
6 changes: 3 additions & 3 deletions examples/hacker-news-api/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2357,9 +2357,9 @@ rechoir@^0.6.2:
dependencies:
resolve "^1.1.6"

redux-tiles@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/redux-tiles/-/redux-tiles-0.8.0.tgz#7e3287a6e0335cd697be7616f79c7b320c1e4536"
redux-tiles@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/redux-tiles/-/redux-tiles-0.9.0.tgz#699e15489b0b0a31ecc9ffff8a28d0aebab47762"
dependencies:
redux "^3.6.0"

Expand Down
2 changes: 1 addition & 1 deletion examples/todomvc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"react-dom": "^15.5.4",
"react-redux": "^5.0.5",
"redux": "^3.6.0",
"redux-tiles": "^0.8.0",
"redux-tiles": "^0.9.0",
"todomvc-app-css": "^2.1.0"
},
"devDependencies": {
Expand Down
22 changes: 7 additions & 15 deletions examples/todomvc/src/tiles/todos.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export const filterTile = createSyncTile({
export const todosTile = createSyncTile({
type: ['todos', 'list'],
fns: {
add: ({ params, selectors, getState }) => {
const list = selectors.todos.list(getState());
add: ({ params, getData }) => {
const list = getData();
const newItem = {
...params,
completed: false,
Expand All @@ -34,21 +34,13 @@ export const todosTile = createSyncTile({

return list.concat(newItem);
},
remove: ({ params, selectors, getState }) => {
const list = selectors.todos.list(getState());
return list.filter(item => item.id !== params.id);
},
toggle: ({ params, selectors, getState }) => {
const list = selectors.todos.list(getState());
return list.map(todo => todo.id === params.id
remove: ({ params, getData }) => getData().filter(item => item.id !== params.id),
toggle: ({ params, getData }) => getData().map(
todo => todo.id === params.id
? { ...todo, completed: !todo.completed }
: todo
);
},
clearCompleted: ({ params, selectors, getState }) => {
const list = selectors.todos.list(getState());
return list.filter(({ completed }) => completed === false);
}
),
clearCompleted: ({ params, getData }) => getData().filter(({ completed }) => completed === false)
},
initialState: [],
});
Expand Down
6 changes: 3 additions & 3 deletions examples/todomvc/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4962,9 +4962,9 @@ reduce-function-call@^1.0.1:
dependencies:
balanced-match "^0.4.2"

redux-tiles@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/redux-tiles/-/redux-tiles-0.8.0.tgz#7e3287a6e0335cd697be7616f79c7b320c1e4536"
redux-tiles@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/redux-tiles/-/redux-tiles-0.9.0.tgz#699e15489b0b0a31ecc9ffff8a28d0aebab47762"
dependencies:
redux "^3.6.0"

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "redux-tiles",
"version": "0.8.0",
"version": "0.9.0",
"description": "Library to create and easily compose redux pieces together in less verbose manner",
"jsnext:main": "lib/es2015/index.js",
"module": "lib/es2015/index.js",
Expand Down
20 changes: 15 additions & 5 deletions src/tiles/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Dispatch } from 'redux';
import { createType } from '../helpers';
import { DEFAULT_REDUCER } from './selectors';
import { createType, ensureArray } from '../helpers';
import { getTopReducer } from './selectors';
import { IAsyncActionTypes, IPromiseObject, ISyncActionTypes } from './types';

interface IProcessedMiddleware {
Expand Down Expand Up @@ -117,10 +117,20 @@ export function createResetAction({ type }: { type: string }): Function {
return handleMiddleware(({ dispatch }: { dispatch: Dispatch<any> }) => dispatch({ type }));
}

export function syncAction({ SET, fn, nesting }: ISyncActionTypes): FnResult {
return handleMiddleware(({ dispatch, getState, ...middlewares }: any, params: any) => {
export function syncAction({ SET, fn, nesting, type }: ISyncActionTypes): FnResult {
return handleMiddleware(({ dispatch, getState, selectors, ...middlewares }: any, params: any) => {
const path: string[]|null = nesting ? nesting(params) : null;
const data = fn({ params, dispatch, getState, ...middlewares });

const topReducer = getTopReducer();
const nestedNames: string[] = ensureArray(type);
const topReducerArray: string[] = Boolean(topReducer) ? [topReducer] : [];

const selectorFn = topReducerArray
.concat(nestedNames)
.reduce((selectors, key) => selectors[key], selectors);

const getData = () => selectorFn(getState(), params);
const data = fn({ params, dispatch, getData, getState, ...middlewares });

return dispatch({
type: SET,
Expand Down
1 change: 1 addition & 0 deletions src/tiles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export function createSyncTile(params: ISyncTileParams): ITile {
const selectors: ISelectors = createSelectors(selectorParams);

const actionParams: ISyncActionTypes = {
type,
SET: types.SET,
nesting,
fn
Expand Down
4 changes: 4 additions & 0 deletions src/tiles/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export function changeDefaultReducer(newReducer: string): void {
DEFAULT_REDUCER = newReducer;
}

export function getTopReducer(): string {
return DEFAULT_REDUCER;
}

function checkValue(result: any, defaultValue?: any): {} {
return result === undefined || result === null ? defaultValue : result;
}
Expand Down
1 change: 1 addition & 0 deletions src/tiles/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export interface IAsyncActionTypes {
}

export interface ISyncActionTypes {
type: string|string[],
SET: string;
fn: Function;
nesting: ((params: any) => string[])|undefined;
Expand Down
56 changes: 53 additions & 3 deletions test/actions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,11 @@ test('syncAction should dispatch processedData immediately', () => {

const dispatch = spy();
const getState = () => {};
const selectors = {
type: () => {}
}

action(params)({ dispatch, getState });
action(params)({ dispatch, getState, selectors });

const call = dispatch.getCall(0);
expect(call.args[0].payload.data).toEqual(params);
Expand Down Expand Up @@ -242,11 +245,58 @@ test('createSyncTile should execute functions from fns correctly', () => {
});

const dispatch = spy();
const middlewares = { dispatch };
const selectors = {
some: () => {}
};
const middlewares = { dispatch, selectors };
const params = { some: 123 };
tile.action.add(params)(middlewares);

const arg = dispatch.getCall(0).args[0];

expect(arg.payload.data).toBe(params);
});
});

test('createSyncTile should have getData fn', () => {
const tile = createSyncTile({
type: ['some'],
fn: spy()
});

const dispatch = () => {};
const selectors = {
some: spy()
};
const getState = () => {};
const middlewares = { dispatch, selectors, getState };
const params = { some: 123 };
tile.action(params)(middlewares);

const arg = tile.reflect.fn.getCall(0).args[0];
arg.getData();

expect(selectors.some.calledOnce).toBe(true);
});

test('getData fn in createSyncTile should handle nested types', () => {
const tile = createSyncTile({
type: ['some', 'additional'],
fn: spy()
});

const dispatch = () => {};
const selectors = {
some: {
additional: spy()
}
};
const getState = () => {};
const middlewares = { dispatch, selectors, getState };
const params = { some: 123 };
tile.action(params)(middlewares);

const arg = tile.reflect.fn.getCall(0).args[0];
arg.getData();

expect(selectors.some.additional.calledOnce).toBe(true);
});
Loading

0 comments on commit 1122d42

Please sign in to comment.