As you’ve learned through Redux
you’ve probably heard “keep the state the simplest, and use it when you need it,” as part of that lesson you may have also used the redux selector
.
A selector function
takes the input
state
and returns a desired value based on that state
. For example
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">const</span> <span class="token function-variable function">selectEntities</span> <span class="token operator">=</span> <span class="token parameter">state</span> <span class="token operator">=></span> state <span class="token punctuation">.</span> entities <span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">selectItemIds</span> <span class="token punctuation">(</span> <span class="token parameter">state</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> state <span class="token punctuation">.</span> items <span class="token punctuation">.</span> <span class="token function">map</span> <span class="token punctuation">(</span> <span class="token parameter">item</span> <span class="token operator">=></span> item <span class="token punctuation">.</span> id <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">const</span> <span class="token function-variable function">selectSomeSpecificField</span> <span class="token operator">=</span> <span class="token parameter">state</span> <span class="token operator">=></span> state <span class="token punctuation">.</span> some <span class="token punctuation">.</span> deeply <span class="token punctuation">.</span> nested <span class="token punctuation">.</span> field <span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">selectItemsWhoseNamesStartWith</span> <span class="token punctuation">(</span> <span class="token parameter">items <span class="token punctuation">,</span> namePrefix</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> filteredItems <span class="token operator">=</span> items <span class="token punctuation">.</span> <span class="token function">filter</span> <span class="token punctuation">(</span> <span class="token parameter">item</span> <span class="token operator">=></span> item <span class="token punctuation">.</span> name <span class="token punctuation">.</span> <span class="token function">startsWith</span> <span class="token punctuation">(</span> namePrefix <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> filteredItems <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
You can use the selector
everywhere as long as it’s a component
, a selector
usually starts with a prefix
get [something] or select [something] or it can be a suffix
[something] selector .
The rendering:
- When an
action
isdispatch
, theuseSelector
will perform a comparison between the previous result and the current one, otherwise the component is forciblyre-render
. useSelector
uses===
comparison, not theshallow compare
method- Reasons for using
useSelector
:- Reuse,
selector
can be used in many places, many differentcomponent
without re-declaring - Lean, we have a
state
car
containingname
,brand
,year
, if you want to getbrand
, justgetBrandCar
selector is easy to understand. - Update, when the
redux store
structure
changes, we just need to update theselector
again
12345678910111213141516<span class="token comment">// Previous</span><span class="token operator">...</span>store <span class="token operator">=</span> <span class="token punctuation">{</span>car <span class="token operator">:</span> <span class="token punctuation">{</span> id <span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">,</span> brand <span class="token operator">:</span> <span class="token string">"Vinfast"</span> <span class="token punctuation">,</span> name <span class="token operator">:</span> <span class="token string">"Lux A"</span> <span class="token punctuation">,</span> year <span class="token operator">:</span> <span class="token string">"2019"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">// selector</span><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">getCarBrand</span> <span class="token operator">=</span> <span class="token parameter">state</span> <span class="token operator">=></span> state <span class="token punctuation">.</span> car <span class="token punctuation">.</span> brand<span class="token comment">//==> After</span><span class="token operator">...</span>store <span class="token operator">=</span> <span class="token punctuation">{</span>car <span class="token operator">:</span> <span class="token punctuation">{</span> id <span class="token operator">:</span> <span class="token number">2</span> <span class="token punctuation">,</span> company <span class="token operator">:</span> <span class="token string">"Vinfast"</span> <span class="token punctuation">,</span> name <span class="token operator">:</span> <span class="token string">"Lux A"</span> <span class="token punctuation">,</span> year <span class="token operator">:</span> <span class="token string">"2019"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">//selector</span><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">getCarCompany</span> <span class="token operator">=</span> <span class="token parameter">state</span> <span class="token operator">=></span> state <span class="token punctuation">.</span> car <span class="token punctuation">.</span> company - Reuse,
Use:
Since the selector
uses the “===” comparison method, if the array
or object
returned through the computation, the component will be trigger re-render
. While re-render
even though the data
not changed, the selectFilteredSortedTransformedData
function is still called:
1 2 3 4 5 6 7 | <span class="token keyword">const</span> <span class="token function-variable function">selectFilteredSortedTransformedData</span> <span class="token operator">=</span> <span class="token parameter">state</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> filteredData <span class="token operator">=</span> <span class="token function">expensiveFiltering</span> <span class="token punctuation">(</span> state <span class="token punctuation">.</span> data <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> sortedData <span class="token operator">=</span> <span class="token function">expensiveSorting</span> <span class="token punctuation">(</span> filteredData <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> transformedData <span class="token operator">=</span> <span class="token function">expensiveTransformation</span> <span class="token punctuation">(</span> sortedData <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> transformedData <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
How to solve the problem, using reselect :
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">const</span> <span class="token function-variable function">selectSomeData</span> <span class="token operator">=</span> <span class="token parameter">state</span> <span class="token operator">=></span> state <span class="token punctuation">.</span> someData <span class="token punctuation">;</span> <span class="token keyword">const</span> selectFilteredSortedTransformedData <span class="token operator">=</span> <span class="token function">createSelector</span> <span class="token punctuation">(</span> selectSomeData <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">someData</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> filteredData <span class="token operator">=</span> <span class="token function">expensiveFiltering</span> <span class="token punctuation">(</span> someData <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> sortedData <span class="token operator">=</span> <span class="token function">expensiveSorting</span> <span class="token punctuation">(</span> filteredData <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> transformedData <span class="token operator">=</span> <span class="token function">expensiveTransformation</span> <span class="token punctuation">(</span> sortedData <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> transformedData <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
Note:
- If the
selector
has aninput
as acomponent
prop
, put theselector
outside of thecomponent
1234567891011121314<span class="token operator">...</span><span class="token keyword">const</span> <span class="token function-variable function">selectCar</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter">state <span class="token punctuation">,</span> carId</span> <span class="token punctuation">)</span> <span class="token operator">=></span> cars <span class="token punctuation">.</span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token parameter">car</span> <span class="token operator">=></span> car <span class="token punctuation">.</span> id <span class="token operator">===</span> carId <span class="token punctuation">)</span> <span class="token punctuation">;</span><span class="token keyword">const</span> selectCarById <span class="token operator">=</span> <span class="token function">createSelector</span> <span class="token punctuation">(</span><span class="token punctuation">[</span> selectCar <span class="token punctuation">]</span> <span class="token punctuation">,</span><span class="token parameter">car</span> <span class="token operator">=></span> <span class="token function">expensiveTransformation</span> <span class="token punctuation">(</span> car <span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">;</span><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">CarDetails</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter"><span class="token punctuation">{</span> carId <span class="token punctuation">}</span></span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token keyword">const</span> car <span class="token operator">=</span> <span class="token function">useSelector</span> <span class="token punctuation">(</span> <span class="token parameter">state</span> <span class="token operator">=></span> <span class="token function">selectCarById</span> <span class="token punctuation">(</span> state <span class="token punctuation">,</span> carId <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span><span class="token keyword">return</span> <span class="token operator"><</span> div <span class="token operator">></span> <span class="token punctuation">{</span> car <span class="token punctuation">.</span> name <span class="token punctuation">}</span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span><span class="token punctuation">}</span> <span class="token punctuation">;</span> - Multiple
instance
ofcomponent
: When aselector
is used in multipleinstance
of acomponent
and also depends onprop
, e.g.123<span class="token operator"><</span> CarDetails carId <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token number">1</span> <span class="token punctuation">}</span> <span class="token operator">></span><span class="token operator"><</span> CarDetails carId <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token number">2</span> <span class="token punctuation">}</span> <span class="token operator">></span>
What we need to do now is make sure eachinstance component
has a separateinstance selector
, as in the above example it is not correct because the twoinstance
are pointing to the sameinstance selector
.Solution, use
useMemo
1234567891011121314<span class="token operator">...</span><span class="token keyword">const</span> <span class="token function-variable function">selectCar</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter">state <span class="token punctuation">,</span> carId</span> <span class="token punctuation">)</span> <span class="token operator">=></span> cars <span class="token punctuation">.</span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token parameter">car</span> <span class="token operator">=></span> car <span class="token punctuation">.</span> id <span class="token operator">===</span> carId <span class="token punctuation">)</span> <span class="token punctuation">;</span><span class="token keyword">const</span> selectCarById <span class="token operator">=</span> <span class="token function">createSelector</span> <span class="token punctuation">(</span><span class="token punctuation">[</span> selectCar <span class="token punctuation">]</span> <span class="token punctuation">,</span><span class="token parameter">car</span> <span class="token operator">=></span> <span class="token function">expensiveTransformation</span> <span class="token punctuation">(</span> car <span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">;</span><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">CarDetails</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter"><span class="token punctuation">{</span> carId <span class="token punctuation">}</span></span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token keyword">const</span> selectCarMemo <span class="token operator">=</span> <span class="token function">useMemo</span> <span class="token punctuation">(</span> selectCarById <span class="token punctuation">,</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span><span class="token keyword">const</span> car <span class="token operator">=</span> <span class="token function">useSelector</span> <span class="token punctuation">(</span> <span class="token parameter">state</span> <span class="token operator">=></span> <span class="token function">selectCarMemo</span> <span class="token punctuation">(</span> state <span class="token punctuation">,</span> carId <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span><span class="token keyword">return</span> <span class="token operator"><</span> div <span class="token operator">></span> <span class="token punctuation">{</span> car <span class="token punctuation">.</span> name <span class="token punctuation">}</span> <span class="token operator"><</span> <span class="token operator">/</span> div <span class="token operator">></span><span class="token punctuation">}</span> <span class="token punctuation">;</span>
And now each instance ofCarDetails
has an instance of a differentselector