-
-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Suport createSelector #39
Comments
this seems to be a good idea if I want to customize some basic primitive types, some uses cases I can think of are like formatting a store value for UI, like showing price with currency or adding discount oven name concatenation. But one thing which is not clear is what's the bigger use case, since teaful already gives us fragmented access to store and we can do the memoization in React easily(assuming its same for other framework), why do this selector thing which is just basic primitive value check |
@Chetan-unbounce In fact it would be very similar to An example: const useExpensiveCalc = createSelector(
() => useStore.items,
(val) => expensiveCalc(val)
);
function Component1() {
const val = useExpensiveCalc()
// ...
}
function Component2() {
const val = useExpensiveCalc()
// ...
}
function Component3() {
const [items] = useStore.items()
const val = useMemo(() => expensiveCalc(items), [items])
// ...
}
function Component4() {
const [items] = useStore.items()
const val = useMemo(() => expensiveCalc(items), [items])
// ...
}
export default function App() {
return (
<>
<Component1 />
<Component2 />
<Component3 />
<Component4 />
</>
)
} At the first render:
In other rerenders for other things:
After a re-render for items update:
This is a very specific case and only makes sense for very complex calculations and heavy applications. So it does not make the createStore totally necessary. In order not to increase the size of the library since many people would not use it if anything I would upload it in a separate package. However, for the moment it's only a proposal, it would be necessary to do several tests and to be totally sure of the results. |
Perhaps the |
I've modified it a little bit to accept as many proxy entries as you want to consume: Examples: const useExpensiveCalc = createSelector(
() => useStore.items,
(items) => expensiveCalc(items)
); const useExpensiveCalc = createSelector(
() => useStore.items,
() => useStore.user,
(items, user) => expensiveCalc(items, user)
); const useExpensiveCalc = createSelector(
() => useStore.items,
() => useStore.user,
() => useStore.cart.price,
(items, user, price) => expensiveCalc(items, user, price)
); Example of function createSelector(...proxies) {
const calc = proxies.pop();
const cache = new Map();
let calculated;
return () => {
let inCache = true;
const values = proxies.map((p) => {
const [value] = p()();
inCache &= cache.has(p) && cache.get(p) === value;
return value;
});
if (!inCache) {
calculated = calc(...values);
proxies.forEach((p, index) => cache.set(p, values[index]));
}
return calculated;
};
} |
The example works, because we are using the same instance of the function returned from createSelector. But if we try to make the logic generic and move it to a new file like const useSelector = (stateFun, cb) => {
return createSelector(stateFun, cb); // same function we have just takes in custom state val and callback
};
export default useSelector; now if we try to import this in two different files we would get different instance of the returned function and the lexical scope will be different and the caching won't work. @aralroca can u add an example with the above use case too, where we make it generic and import it from a different file into different components in different files. |
@Chetan-unbounce I did a little modification to support to define the I hope this works for you, if not we will look for a better way to implement it. At the moment it is just a draft. Way 1export const useCalculatedValue = createSelector(
() => useStore.cart.price,
() => useStore.username,
(price, username) => `${username} - ${price * 3}`
); And to use the selector: const calculatedValue = useCalculatedValue() Way 2export const useSelector = createSelector(); And to use the selector: const calculatedValue = useSelector(
() => useStore.cart.price,
() => useStore.username,
(price, username) => `${username} - ${price * 3}`
); createSelector:function createSelector(...proxiesCreateSelector) {
const cache = new Map();
let calculated;
return (...proxiesSelector) => {
const proxies = proxiesCreateSelector.length
? proxiesCreateSelector
: proxiesSelector;
const calc = proxies.pop();
let inCache = true;
const values = proxies.map((p) => {
const [value] = p()();
inCache &= cache.has(p) && cache.get(p) === value;
return value;
});
if (!inCache) {
calculated = calc(...values);
proxies.forEach((p, index) => cache.set(p, values[index]));
}
return calculated;
};
} |
I think it is better to create the hooks directly with the createSelector. Why do you need a separate useSelector? |
Create hooks for calculated store values:
Example:
The
val + (val * 0.21)
calc only will be executed once for all the components that use theusePriceWithIva();
hook. It only will be calculated again whencart.price
change and is going to do a rerender for all these components that use this hook.For this simple calculation, it does not make any sense to use the
createSelector
, but it can be useful for very complex calculations.And is also possible to do the same with helpers:
Example of complete example:
And an example of
createSelector
:The text was updated successfully, but these errors were encountered: