Skip to content

Commit

Permalink
Merge pull request #7 from kouhin/release/v2.0.0
Browse files Browse the repository at this point in the history
Release/v2.0.0
  • Loading branch information
kouhin authored Mar 8, 2017
2 parents a9b8dfd + 6ec7ce5 commit 384985e
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 54 deletions.
9 changes: 1 addition & 8 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
{
"presets": [
[
"latest",
{
"es2015": {
"loose": true
}
}
],
"env",
"stage-0"
],
"plugins": [
Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const increment = () => {
};

// This is a memoized action creator.
const memoizeIncrement = memoize({ ttl: 100 })(increment);
const memoizeIncrement = memoize({ ttl: 100 }, increment);

// Reducer
function counter(state = 0, action) {
Expand Down Expand Up @@ -90,7 +90,7 @@ const fetchUserSuccess = (user) => {
let creatorCalled = 0;
let thunkCalled = 0;

const fetchUserRequest = memoize({ ttl: 1000 })((username) => {
const fetchUserRequest = memoize({ ttl: 1000 }, (username) => {
creatorCalled += 1;
return (dispatch, getState) => {
thunkCalled += 1;
Expand Down Expand Up @@ -129,31 +129,32 @@ Promise.all([promise1, promise2])

## API

### memoize(opts)(actionCreator)
### memoize(opts, actionCreator)

Memoize actionCreator and returns a memoized actionCreator. When dispatch action that created by memorized actionCreator, it will returns a Promise.

#### Arguments

- `opts` _Object_
- `opts` _Object | number <optional>_
- `ttl` _Number|Function_: The time to live for cached action creator. When `ttl` is a function, `getState` will be passed as argument, and it must returns a number.
- `enabled` _Boolean|Function_: Whether use memorized action creator or not. When `false`, cache will be ignored and the result of original action creator will be dispatched without caching. When `enabled` is a function, `getState` will be passed argument, and it must returns a boolean.
- `isEqual`: arguments of action creator will be used as the map cache key. It uses lodash.isEqual to find the existed cached action creator. You can customize this function.
- If `opts` is a number, the numbrer specifies the ttl.

#### Returns

- (Promise): will be resolved with the result of original actionCreator.

### createMemoizeMiddleware(opts)
### createMemoizeMiddleware(globalOpts, disableTTL)

Create a redux [middleware](http://redux.js.org/docs/advanced/Middleware.html).

#### Arguments

- `opts` _Object_
- disableTTL _Boolean_: The default value is `true` on server and `false` on browser. By default, cached action creator will not be evicted by setTimeout with TTL on server in order to prevent memory leak. You can enable it for test purpose.
- globalOptions _Object_: Default opts for memorize().
- **Default**: `{ ttl: 0, enabled: true, isEqual: lodash.isEqual }`
- `globalOpts` _Object <optional>_
- _Object_: Default opts for memorize().
- **Default**: `{ ttl: 0, enabled: true, isEqual: lodash.isEqual }`]
- There is another options `disableTTL`. The default value is `true` on server and `false` on browser. By default, cached action creator will not be evicted by setTimeout with TTL on server in order to prevent memory leak. You can enable it for test purpose.

#### Returns

Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "redux-memoize",
"version": "1.1.0",
"version": "2.0.0",
"description": "Memoize action creator for redux, and let you dispatch common/thunk/promise/async action whenever you want to, without worrying about duplication",
"main": "lib/index.js",
"directories": {
Expand Down Expand Up @@ -45,15 +45,15 @@
"babel-core": "^6.23.1",
"babel-eslint": "^7.1.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-latest": "^6.22.0",
"babel-preset-env": "^1.2.1",
"babel-preset-stage-0": "^6.22.0",
"chai": "^3.5.0",
"eslint-config-airbnb-deps": "^14.1.0",
"eslint-plugin-babel": "^4.0.1",
"jest": "^18.1.0",
"eslint-plugin-babel": "^4.1.1",
"jest": "^19.0.2",
"redux": "^3.6.0",
"redux-thunk": "^2.2.0",
"rimraf": "^2.5.4"
"rimraf": "^2.6.1"
},
"dependencies": {
"babel-runtime": "^6.23.0",
Expand Down
32 changes: 21 additions & 11 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import lodashIsEqual from 'lodash/isEqual';
const ACTION_TYPE = '@redux-memoize/action';

const DEFAULT_META = {
ttl: 0,
ttl: 200,
enabled: true,
isEqual: lodashIsEqual,
};
Expand All @@ -27,18 +27,19 @@ function deepGet(map, args, isEqual) {
}

export default function createMemoizeMiddleware(options = {}) {
const opts = {
const {
// default disableTTL is true on server side, to prevent memory leak (use GC to remove cache)
disableTTL: !canUseDOM,
...options,
};
disableTTL = !canUseDOM,
...globalOptions
} = options;

const cache = new Map();
const middleware = ({ dispatch, getState }) => next => (action) => {
if (typeof action === 'object' && action.type === ACTION_TYPE) {
const { fn, args } = action.payload;
const { ttl, enabled, isEqual } = {
...DEFAULT_META,
...(options.globalOptions || {}),
...globalOptions,
...(action.meta || {}),
};
let taskCache = cache.get(fn);
Expand All @@ -55,7 +56,7 @@ export default function createMemoizeMiddleware(options = {}) {
const finalTTL = typeof ttl === 'function' ? ttl(getState) : ttl;
if (finalTTL) {
taskCache.set(args, task);
if (!opts.disableTTL) {
if (!disableTTL) {
setTimeout(() => {
taskCache.delete(args);
}, finalTTL);
Expand All @@ -81,15 +82,24 @@ export default function createMemoizeMiddleware(options = {}) {
return middleware;
}

export const memoize = options => (fn) => {
if (typeof fn !== 'function') {
export function memoize(opts, fn) {
let func;
let options;
if (arguments.length < 2) {
options = null;
func = opts;
} else {
options = typeof opts === 'object' ? opts : { ttl: opts };
func = fn;
}
if (typeof func !== 'function') {
throw new Error('Not a function');
}
return (...args) => {
const action = {
type: ACTION_TYPE,
payload: {
fn,
fn: func,
args,
},
};
Expand All @@ -98,4 +108,4 @@ export const memoize = options => (fn) => {
}
return action;
};
};
}
36 changes: 15 additions & 21 deletions test/middleware.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,23 @@ describe('memoize', () => {
}

describe('memoize()', () => {
it('memoize() must return a function', () => {
expect(typeof memoize()).toBe('function');
});

it('must throw if argument is not a function', () => {
expect(() => {
memoize()({});
memoize({});
}).toThrow();
expect(() => {
memoize()();
memoize();
}).toThrow();
expect(() => {
memoize()(() => {});
memoize(() => {});
}).not.toThrow();
});
});

describe('options', () => {
it('when options is not specified, must return an action without meta', () => {
const args = [1, 2, '3'];
expect(memoize()(actionCreator)(...args)).toEqual({
expect(memoize(actionCreator)(...args)).toEqual({
type: '@redux-memoize/action',
payload: {
fn: actionCreator,
Expand All @@ -51,7 +47,7 @@ describe('memoize', () => {
const args = [1, 2, '3'];
expect(memoize({
ttl: 100,
})(actionCreator)(...args)).toEqual({
}, actionCreator)(...args)).toEqual({
type: '@redux-memoize/action',
payload: {
fn: actionCreator,
Expand All @@ -65,7 +61,7 @@ describe('memoize', () => {
const ttl = getState => getState().ttl;
expect(memoize({
ttl,
})(actionCreator)(...args)).toEqual({
}, actionCreator)(...args)).toEqual({
type: '@redux-memoize/action',
payload: {
fn: actionCreator,
Expand All @@ -82,7 +78,7 @@ describe('memoize', () => {
const isEqual = (args1, args2) => (args1 === args2);
expect(memoize({
isEqual,
})(actionCreator)(...args)).toEqual({
}, actionCreator)(...args)).toEqual({
type: '@redux-memoize/action',
payload: {
fn: actionCreator,
Expand Down Expand Up @@ -192,7 +188,7 @@ describe('unit test', () => {
return state;
}
}
const createThunk = memoize({ ttl: 200 })((num) => {
const createThunk = memoize({ ttl: 200 }, (num) => {
thunkCreatorCalled += 1;
return {
type: 'INCREMENT',
Expand Down Expand Up @@ -234,7 +230,7 @@ describe('unit test', () => {
return state;
}
}
const createThunk = memoize({ ttl: 50 })(num => ({
const createThunk = memoize({ ttl: 50 }, num => ({
type: 'INCREMENT',
payload: num,
}));
Expand Down Expand Up @@ -298,16 +294,14 @@ describe('unit test', () => {
return state;
}
}
const createThunk = memoize()(num => ({
const createThunk = memoize(num => ({
type: 'INCREMENT',
payload: num,
}));

const memoizeMiddleware = createMemoizeMiddleware({
disableTTL: false,
globalOptions: {
ttl: 50,
},
ttl: 50,
});

const store = applyMiddleware(
Expand Down Expand Up @@ -371,7 +365,7 @@ describe('unit test', () => {
return state;
}
}
const createThunk = memoize({ ttl: 200 })((num) => {
const createThunk = memoize({ ttl: 200 }, (num) => {
thunkCreatorCalled += 1;
return (dispatch) => {
thunkCalled += 1;
Expand Down Expand Up @@ -428,7 +422,7 @@ describe('unit test', () => {
return state;
}
}
const createThunk = memoize({ ttl: 100 })((num) => {
const createThunk = memoize({ ttl: 100 }, (num) => {
thunkCreatorCalled += 1;
return (dispatch) => {
thunkCalled += 1;
Expand Down Expand Up @@ -526,7 +520,7 @@ describe('unit test', () => {
const enabled = getState().memoizeEnabled;
return enabled === undefined ? true : enabled;
},
})((num) => {
}, (num) => {
incrementCalled += 1;
return {
type: 'INCREMENT',
Expand Down Expand Up @@ -584,7 +578,7 @@ describe('unit test', () => {
const ttl = getState().ttl;
return typeof ttl === 'undefined' ? 0 : ttl;
},
})((num) => {
}, (num) => {
incrementCalled += 1;
return {
type: 'INCREMENT',
Expand Down

0 comments on commit 384985e

Please sign in to comment.