async-selector
Advanced tools
Comparing version 1.0.10 to 1.0.11
@@ -792,1 +792,24 @@ import createAsyncSelector from '../src/index'; | ||
}); | ||
test('throttled and forced', done => { | ||
let state = {employees: ['Mark Metzger'], maxAge: 1}; | ||
let result = null | ||
const throttle = f => _.debounce(f, 150); | ||
const onCancel = (r, n, a) => {result=[r,n,a]} | ||
const ages = createAsyncSelector( | ||
{...params2, throttle, onCancel}, | ||
s => s.employees, | ||
s => s.maxAge); | ||
ages(state); | ||
ages.forceUpdate(state); | ||
ages.forceUpdate(state); | ||
setTimeout(() => { | ||
try { | ||
expect(deepEqual(result, null)).toBe(true); | ||
} catch (e) { | ||
done.fail(e) | ||
} | ||
done(); | ||
}, 200); | ||
}); |
{ | ||
"name": "async-selector", | ||
"version": "1.0.10", | ||
"version": "1.0.11", | ||
"description": "Select values from databases using asynchronous selectors.", | ||
@@ -5,0 +5,0 @@ "main": "./dist/index.js", |
A simple, lightweight library inspired by [reselect](https://github.com/reduxjs/reselect) which allows you select data out of a remote database almost as easily as you would from local state. This library will normally be used in conjunction with redux and reselect but it has no dependencies. | ||
# Installation | ||
``` | ||
npm install --save async-selector | ||
``` | ||
# What It solves | ||
A normal (naive) approach to handling fetching data from a database in redux would be | ||
- Initiate a request is response to some event (app loaded, component mounted, search text changed, etc) | ||
- dispatch an action to change a variable in state so you can render a loading message | ||
- handle a promise rejection in a similar way | ||
- handle a promise resolution by populating state with the new data | ||
A normal (naive) approach to handling fetching data from a database in redux would be: | ||
- Initiate a request in response to some event (app loaded, component mounted, search text changed, etc). | ||
- dispatch an action to change a variable in state so you can render a loading message. | ||
- handle a promise rejection in a similar way. | ||
- handle a promise resolution by populating state with the new data. | ||
@@ -13,7 +18,7 @@ ### Problems | ||
- An old request could potential overwrite the data of a newer request if you don't handle that edge case. | ||
- During the time between the request being sent and the response received, you might be rendering stale data. | ||
- Having many variables in state and associated actions/reducers for the query status is a lot of redundant, tedious code. | ||
- If you have requests that depend on previous responses, this could result in callback hell and/or difficulty in reuse of the first response's data. | ||
- During the time between the request being sent and the response received, you might be rendering stale data. In some cases, this can be a dangerous bug. | ||
- Having many state variables/actions/reducers/etc for every query is a lot of redundant, tedious code. | ||
- If you have requests that depend on previous responses, this could result in callback hell and/or difficulty in future reuse of the first response's data. | ||
- There is no guarantee that multiple duplicate requests won't be made. | ||
- Sending queries in response to user actions can result in brittle code and isn't really in the spirit of redux/reselect (Unless it's actually necessary). For example, if you call a function in a server based on many user inputs, you will have to write code to send a query for every input field. | ||
- Sending queries in response to user actions can result in brittle code and isn't really in the spirit of redux/reselect. For example, if you call a function in a server that uses many user inputs, you will have to write code to send a query for every input field. This scales poorly with the complexity of your application. | ||
@@ -90,3 +95,3 @@ # Async selectors to the rescue! | ||
### Usage in redux | ||
A serious problem with the above code is that when the promise resolves, the app doesn't re-render to show the new data. Instead of simply logging the employees in the onResolve callback, we need to dispatch an action to tell the app to re-render and call the selector. This is a bit of a hack because nothing in the state was changed. One thing to make sure of is that the action changes state in some way or a re-render event won't be triggered. Another thing to be careful to avoid is recursion resulting from the action causing the inputs of the selector to change. | ||
A serious problem with the above code is that when the promise resolves, the app doesn't re-render to show the new data. Instead of simply logging the employees in the onResolve callback, we need to dispatch an action to tell the app to re-render and call the selector and get the new value. This is a bit of a hack because nothing in the state was changed. One thing to make sure of is that the action changes state in some way or a re-render event won't be triggered. Another thing to be careful to avoid is recursion resulting from the action causing the inputs of the selector to change. | ||
@@ -124,2 +129,3 @@ ```js | ||
const employeeAges = createAsyncSelector(params2, [employees]); | ||
console.log('Ages:', employeeAges(store.getState())) | ||
``` | ||
@@ -132,7 +138,7 @@ | ||
function buttonClicked() { | ||
employeeAges(store.getState(), true) // "true" indicates that the selector should create a new promise regardless of whether the inputs changed | ||
const result = employeeAges.forceUpdate(store.getState()) // the selector will create a new promise regardless of whether the inputs changed. It will always return an object in the "isWaiting" state. | ||
} | ||
``` | ||
By passing true as the second parameter of the selector, the selector will be called as if the inputs changed thus automatically creating a new promise. | ||
The selector will be called as if the inputs changed thus automatically creating a new promise. With this technique, an async selector could simply be treated like a memoized function and state variable combo. | ||
@@ -161,2 +167,5 @@ ### Throttling queries | ||
### Usage across multiple instances of a component | ||
Just like in reselect, an async selector can take in two variables (for example, global state and component props). Reselect has examples of this [here](https://github.com/reduxjs/reselect#selectorstodoselectorsjs-1). | ||
# Documentation | ||
@@ -179,5 +188,6 @@ createAsyncSelector takes in two arguments: | ||
## selectors | ||
Each selector is a function that takes in state as its argument just like in reselect. It memoizes its results so the only way for it to return a different value for the same inputs is if it contained a promise that was resolved. An async selector is only different from a normal selector in that you can pass in "forceUpdate" bool as the second parameter to force a promise to be made. | ||
Each selector is a function that takes in state (and optionally a second variable) as its argument just like in reselect. It memoizes its results so the only way for it to return a different value for the same inputs is if it contained a promise that was resolved. An async selector is only different from a normal selector in that you can call ".forceUpdate(state)" of state which will automatically create a new promise and return an object in the "isWaiting" state. | ||
```js | ||
function selector(state, forceUpdate=false) -> any | ||
function selector(state, ?props) -> object | ||
selector.forceUpdate(state, ?props) -> object | ||
``` | ||
@@ -184,0 +194,0 @@ ## params |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
144545
938
230