Closed warren-bank closed 4 years ago
If you don't mind me picking your brain.. I'm on the fence about one topic that may have a slight impact on efficiency.. and I'm curious your thoughts.
Boiling the scenario down as much as possible.. here is the situation:
useMappedState
functionsIf a custom equality function (ex: shallow, deep, etc) was to be introduced to improve efficiency, would it be better to:
useMappedState
.. to prevent the component from re-rendering unnecessarily (ie: every time the global Redux state changes)useMappedState
output.. to memoize this resultcurrently:
useMappedState
uses the default equality function, which depends only on top-level reference
the result of the function that depends on all the useMappedState
output is memoized
Specifically:
in useMappedState
:
I'm debating changing the shape of the options Object to maybe something like:
{
equality: {
forceUpdate: [],
recalculate: ''
}
}
where:
options.equality.forceUpdate
contains an Array of strings (ex: 'shallow', 'deep', null) that is the same length and order as the input-selectors in the input parameters
useMappedState
options.equality.recalculate
is a string (ex: 'shallow', 'deep', null)
useMappedState
output is memoizedshortcuts:
if (typeof options.equality === 'string')
options.equality = {forceUpdate: options.equality, recalculate: options.equality}
if (typeof options.equality.forceUpdate === 'string')
options.equality.forceUpdate = inputSelectors.map(() => options.equality.forceUpdate)
but.. I'm not entirely sure if this is the best strategy
any thoughts?
update: I just made the proposed change. Ultimately, the default behavior is unchanged. This just adds the ability to precisely configure all of the equality functions.. to fine tune (a) when mutation to Redux state should trigger the React component to re-render, and (b) how the the Redux selector is memoized. ..Always good to have knobs and levers for when you need them.
That looks interesting, I'm having a bit of trouble following what it is for, though. I see a lot of hooks being called conditionally which is usually not good, but if it is the same on every call it might be fine. What does this library buy you over just using reselect
directly?
I'd like to keep this library (redux-react-hook) lean, so this should be a separate module. I'm going to close this issue, but we can have further discussion here.
I totally agree with your concern about hooks being called in conditional blocks. While this appears to violate the 1st rule of hooks, this particular usage is safe under the implied contract that:
createReduxSelector(..args)
is always the same every time the React component renders~ (..refer to update 1 below)As for my motivation to experiment with a re-implementation of this library:
Since the purpose of this library is to provide good integration between Redux and React, these 2 missing ingredients (taken together) felt like a good candidate for inclusion. The useMappedState()
method provides a very easy path to do so, since it already does most of the heavy lifting.
useMappedState()
:
useMappedState()
false
:
useMappedState()
is going to force a render updateupdate 1 (correction):
the thing I said about there being an implied contract needs clarification..
createReduxSelector(..args)
outside of the React component...args
are scoped within the closure and cannot be changed.. so the same if/then
block(s) will always be evaluated every time the consolidated function is called
~incomplete/untested~ example (that entirely ignores Redux state for brevity):
const selector = createReduxSelector(
(state, props) => props.msg || "I did , did I",
(msg) => {console.log(msg); return msg.split('').reverse().join('')} // reverse string
)
const Component = (props) => {
let rev_msg = selector(props)
return (
<div>{rev_msg}</div>
)
}
ReactDOM.render(
<Component msg="hello world!" />,
document.getElementById('root')
)
update 2 (live examples @ codesandbox):
To be entirely honest, I only glanced at the recommended methodology because it strikes me as very overly complicated.
If that methodology is feasible, then I would challenge you to fork example # 4 and rewrite it (using only redux-react-hook
and optionally reselect
) to produce the same output.
I'm honestly curious what that solution would look like, and how well it would scale to having more than only one input-selector that derives its value from Redux state.. as this example is a fairly trivial use-case.
update 1:
ok.. I took a closer look, and now I see what's happening in the recommended methodology. I'm going to accept my own challenge, and will post the link to a working example ~(..soon)~.
update 2:
useSelector
tl;dr:
const useSelector = (selector, ...params) => {
return useMappedState(
React.useCallback(state => selector(state, ...params), params)
);
};
//...
const Component = (props) => {
let rev_msg = useSelector(selector, props)
return (
<div>{rev_msg}</div>
)
}
Hi.. it's me again :)
This issue is unrelated to my previous issue #78.. where I introduced a little library that is a light-weight wrapper around the
redux-react-hook
library to add an enhancement touseDispatch()
.This issue pertains to another enhancement that I added to that library, which is a function called
createReduxSelector
which aims to provide a (very nearly) drop-in replacement for Redux reselect.Links to learn more..
I just wanted to let you know about it..
redux-react-hook
library?