Skip to content

Commit

Permalink
Merge pull request #5 from myheritage/change-library-name-and-support…
Browse files Browse the repository at this point in the history
…-object-placeholders

Update library API
  • Loading branch information
perilevy authored Aug 20, 2020
2 parents 7dd38c7 + fd4c909 commit 05d63ec
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 125 deletions.
4 changes: 2 additions & 2 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"presets": ["@babel/preset-env", {"exclude": ["@babel/plugin-transform-typeof-symbol", "transform-regenerator"]}],
"presets": ["@babel/preset-env"],
"env": {
"commonjs": {
"plugins": [
Expand All @@ -10,7 +10,7 @@
"plugins": [
["babel-plugin-transform-es2015-modules-umd", { "loose": true }]
],
"moduleId": "ReduxActionSelector"
"moduleId": "ReduxSelectorAction"
},
"test": {
"plugins": [
Expand Down
8 changes: 4 additions & 4 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
4
],
"indent-legacy": "off",
"init-declarations": "error",
"init-declarations": "off",
"jsx-quotes": "error",
"key-spacing": "error",
"keyword-spacing": [
Expand Down Expand Up @@ -168,7 +168,7 @@
"no-lonely-if": "error",
"no-loop-func": "error",
"no-loss-of-precision": "error",
"no-mixed-operators": "error",
"no-mixed-operators": "off",
"no-mixed-requires": "error",
"no-multi-assign": "error",
"no-multi-spaces": "error",
Expand Down Expand Up @@ -215,7 +215,7 @@
"no-ternary": "off",
"no-throw-literal": "error",
"no-undef-init": "error",
"no-undefined": "error",
"no-undefined": "off",
"no-underscore-dangle": "error",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": "error",
Expand All @@ -239,7 +239,7 @@
"error",
"never"
],
"object-property-newline": "error",
"object-property-newline": ["error", { "allowAllPropertiesOnSameLine": true }],
"object-shorthand": "error",
"one-var": "off",
"one-var-declaration-per-line": "error",
Expand Down
147 changes: 103 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
# Redux Action Selector
# Redux Selector Action
[![npm package][npm-badge]][npm]

Same Redux "selectors" but for actions, inspired by [reselect](https://github.com/reduxjs/reselect).

* Action selectors can be dispatched as any other Redux action.
* Action selectors accept store selectors to eventually inject their output to the given action creator.
* Action selectors reduce the data your "containers" (components connected to redux store) need to pass.
* Selector Actions can be dispatched as any other Redux action.
* Selector Actions accept store selectors to eventually inject their output to the given action creator.
* Selector Actions reduce the data your "containers" (components connected to redux store) need to pass.

An action selector accepts a list of selectors and a regular action creator
A selector-action creator accepts a list of selectors and a regular action creator

**container/actions.js**

```js
import { createActionSelector, getPlaceholder } from 'redux-action-selector';
import { createSelectorAction, getPlaceholder } from 'redux-selector-action';

const getCsrfToken = state => state.csrfToken;
const getCurrency = state => state.currency;
const getLang = state => state.lang;


export const fetchOrder = createActionSelector(
export const fetchOrder = createSelectorAction(
getCsrfToken,
getPlaceholder, // placeholder for order id
getCurrency,
Expand Down Expand Up @@ -49,26 +49,44 @@ const mapDispatchToProps = dispatch => {
}
```

**index.js**

```js
import { applyMiddleware, createStore, compose } from 'redux';
import { reduxSelectorActionMiddleware } from 'redux-selector-action';
import rootReducer from './reducers';
import {fetchOrder} from './container/actions';

const middlewareEnhancer = applyMiddleware(reduxSelectorActionMiddleware);
const composedEnhancers = compose(middlewareEnhancer);
const initialState = undefined;

const store = createStore(rootReducer, initialState, composedEnhancers);

store.dispatch(fetchOrder(123));
```

## Table of Contents

- [Installation](#installation)
- [Motivation for Action Selectors](#motivation-for-action-selectors)
- [Motivation for Selector Actions](#motivation-for-selector-actions)
- [API](#api)
- [`getPlaceholder`](#getplaceholder)
- [`createActionSelector`](#createactionselectorselectors--selectors-resultfunc)
- [`createSelectorAction`](#createactionselectorselectors--selectors-resultfunc)
- [`reduxSelectorActionMiddleware`](#reduxselectoractionmiddleware)
- [FAQ](#faq)
- [Can I use this package without Reselect and Redux?](#q-can-i-use-this-package-without-reselect-and-redux)
- [Can I use this package without Redux?](#q-can-i-use-this-package-without-redux)
- [Can I use this package without Reselect?](#q-can-i-use-this-package-without-reselect)
- [My action accepts many args that can't be injected, should I pass many getPlaceholders?](#q-my-action-accepts-many-args-that-cant-be-injected-should-i-pass-many-getplaceholders)
- [My action accepts an options object which its props can be injected, how can I inject them?](#q-my-action-accepts-an-options-object-which-its-props-can-be-injected-how-can-i-inject-them)
- [How can I test an action selector?](#q-how-can-i-test-an-action-selector)
- [How can I test a selector action?](#q-how-can-i-test-a-selector-action)
- [License](#license)


## Installation
npm install redux-action-selector
npm install redux-selector-action


## Motivation for Action Selectors
## Motivation for Selector Actions
Containers include in most cases some props, which they get just to pass to actions creators.
In order to avoid redundant data (props) passed to those containers, there should be a way for actions to get their data from store.
This way would let us create actions which accept only the data the container holds.
Expand All @@ -91,27 +109,21 @@ Let's fix this.

## API

### getPlaceholder()
This is a built-in selector, which you can use as part of your action creator's selectors.
Once you pass it as a dependency, instead of injecting the output of this selector,
we save its position in the dependency list (selectors) for an arg, which will be sent once you call the action selector.


### createActionSelector(...selectors | [...selectors], resultFunc)
### createSelectorAction(...selectors | [...selectors], resultFunc)

This function accept a list of selectors, or an array of selectors, computes their output against the store's state, and inject them as arguments to the given `resultFunc`.

[`getPlaceholder`](#getplaceholder) selector will be handled separately.

```js
import { createActionSelector, getPlaceholder } from 'redux-action-selector';
import { createSelectorAction, getPlaceholder } from 'redux-selector-action';
import {updateOrderCurrencyAction} from './actions';

const getCsrfToken = state => state.csrfToken;
const getOrderId = state => state.orderId;

// We accept array of selectors too! choose your preferred way.
export const updateOrderCurrency = createActionSelector(
export const updateOrderCurrency = createSelectorAction(
[getCsrfToken, getOrderId, getPlaceholder /* currency */],
updateOrderCurrencyAction,
);
Expand All @@ -120,37 +132,83 @@ export const updateOrderCurrency = createActionSelector(
updateOrderCurrency('USD');
```

### getPlaceholder()
This is a built-in selector, which you can use as part of your action creator's selectors.
Once you pass it as a dependency, instead of injecting the output of this selector,
we save its position in the dependency list (selectors) for an arg, which will be sent once you call the selector action.

In case you have an action creator with an "options" argument (meaning an object which maps arg names to their values),
you can use the following syntax:

```js
import { createSelectorAction, getPlaceholder } from 'redux-selector-action';
import { getCsrfToken, getCurrency, getLang } from './selectors';

export const fetchOrder = createSelectorAction(
// Map the arg names to selectors, then your action creator will get their values:
getPlaceholder({
token: getCsrfToken,
currency: getCurrency,
lang: getLang,
}),
({token, orderId, currency, lang}) => ({
type: 'fetch_order_request',
payload: {
// ...
}
})
);

// fetchOrder({orderId: 123});
```

### reduxSelectorActionMiddleware()
This is a redux middleware which handles our build-in selector actions.
In order to make everything work, you should add it to your store enhancers, the position does not matter.

```js
import { applyMiddleware, createStore, compose } from 'redux';
import { reduxSelectorActionMiddleware } from 'redux-selector-action';
import rootReducer from './reducers';

const middlewareEnhancer = applyMiddleware(reduxSelectorActionMiddleware);
const composedEnhancers = compose(middlewareEnhancer);
const initialState = undefined;

const store = createStore(rootReducer, initialState, composedEnhancers);
```

## FAQ

### Q: Can I use this package without Reselect and Redux?
### Q: Can I use this package without Redux?

A: Yes. This package has no dependencies on any other package, even though it was designed to be used with Reselect and Redux.
A: No. Even though this package has no dependency on Redux, it was designed to be used with Redux.
It means we expect for example that our middleware will be called with Redux store api (store.getState(), store.dispatch()).


### Q: My action accepts many args that can't be injected, should I pass many getPlaceholders?
### Q: Can I use this package without Reselect?

A: Not necessarily. All args you pass to the created action selector will be injected to the placeholders.
But if you pass more args than placeholders, then they will be appended too.
A: Yes. This package has no dependency on Reselect, you can work with any selectors you want, eventually they are just functions that accept state.


### Q: My action accepts an options object which its props can be injected, how can I inject them?
### Q: My action accepts many args that can't be injected, should I pass many getPlaceholders?

A: We are working to support also objects (options argument), it will be available soon.
At the meantime, you can extract them to be regular args if you wish to.
A: Not necessarily. All args you pass to the created selector action will be injected to the placeholders.
But if you pass more args than placeholders, then they will be appended too.


### Q: How can I test an action selector?
### Q: How can I test a selector action?

Every action selector keeps a reference to the given selectors and the action creator, as `.dependencies` and `.resultFunc` respectively.
Every selector action keeps a reference to the given selectors and the action creator, as `.dependencies` and `.resultFunc` respectively.

For example if you have the following action selector:
For example if you have the following selector action:

**src/actionSelectors.js**
**src/selectorActions.js**
```js
export const getFirst = state => 1;
export const getSecond = state => 2;

export const myActionSelector = createActionSelector(
export const mySelectorAction = createSelectorAction(
getFirst,
getSecond,
getPlaceholder,
Expand All @@ -160,19 +218,20 @@ export const myActionSelector = createActionSelector(

You can test it this way:

**test/actionSelectors.js**
**test/selectorActions.js**

```js
import { mySelectorAction } from '../src/selectorActions';

// test the selectors themselves...
test("getFirst", () => { /* ... */ });
test("getSecond", () => { /* ... */ });

test("myActionSelector", () => {
test("mySelectorAction", () => {
// check the dependencies are as expected
assert(myActionSelector.dependencies).toEqual([getFirst, getSecond, getPlaceholder]);
// check the the resultFunc output as expected
assert(myActionSelector.resultFunc(1, 2, 3)).toMatchSnapshot();

assert(mySelectorAction.dependencies).toEqual([getFirst, getSecond, getPlaceholder]);
// check the resultFunc output is as expected
assert(mySelectorAction.resultFunc(1, 2, 3)).toMatchSnapshot();
})
```

Expand All @@ -181,7 +240,7 @@ test("myActionSelector", () => {

[MIT](./LICENSE)

[npm-badge]: https://img.shields.io/npm/v/redux-action-selector.svg?style=flat-square
[npm]: https://www.npmjs.org/package/redux-action-selector
[npm-badge]: https://img.shields.io/npm/v/redux-selector-action.svg?style=flat-square
[npm]: https://www.npmjs.org/package/redux-selector-action


2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "redux-action-selector",
"name": "redux-selector-action",
"version": "1.0.0",
"description": "Action selectors for Redux.",
"description": "Selector Actions for Redux.",
"main": "lib/index.js",
"jsnext:main": "es/index.js",
"module": "es/index.js",
Expand All @@ -10,7 +10,7 @@
"compile:cjs": "better-npm-run compile:cjs",
"compile:es": "babel -d es/ src/",
"compile:umd": "better-npm-run compile:umd",
"compile:umdmin": "uglifyjs dist/redux-action-selector.js -m -o dist/redux-action-selector.min.js",
"compile:umdmin": "uglifyjs dist/redux-selector-action.js -m -o dist/redux-selector-action.min.js",
"lint": "eslint src/ test/",
"lint:fix": "eslint src/ test/ --fix",
"prepublish": "npm run compile",
Expand All @@ -25,7 +25,7 @@
}
},
"compile:umd": {
"command": "mkdirp dist/ && babel -o dist/redux-action-selector.js src/",
"command": "mkdirp dist/ && babel -o dist/redux-selector-action.js src/",
"env": {
"NODE_ENV": "umd"
}
Expand All @@ -45,7 +45,7 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/myheritage/redux-action-selector.git"
"url": "git+https://github.com/myheritage/redux-selector-action.git"
},
"keywords": [
"react",
Expand All @@ -63,9 +63,9 @@
],
"license": "MIT",
"bugs": {
"url": "https://github.com/myheritage/redux-action-selector/issues"
"url": "https://github.com/myheritage/redux-selector-action/issues"
},
"homepage": "https://github.com/myheritage/redux-action-selector#readme",
"homepage": "https://github.com/myheritage/redux-selector-action#readme",
"devDependencies": {
"@babel/cli": "^7.10.5",
"@babel/plugin-transform-modules-commonjs": "^7.10.4",
Expand Down
Loading

0 comments on commit 05d63ec

Please sign in to comment.