What are selectors in redux? - reactjs

I am trying to follow this code in redux-saga
export const getUser = (state, login) => state.entities.users[login]
export const getRepo = (state, fullName) => state.entities.repos[fullName]
Which is then used in the saga like this:
import { getUser } from '../reducers/selectors'
// load user unless it is cached
function* loadUser(login, requiredFields) {
const user = yield select(getUser, login)
if (!user || requiredFields.some(key => !user.hasOwnProperty(key))) {
yield call(fetchUser, login)
}
}
This getUser reducer (is it even a reducer) looks very different from what I would normally expect a reducer to look like.
Can anyone explain what a selector is and how getUser is a reducer and how it fits in with redux-saga?

getUser is not a reducer, it is indeed a selector, that is, a function that knows how to extract a specific piece of data from the store.
Selectors provide an additional layer such that if you altered your store structure and all of a sudden your users were no longer at state.entities.users but instead at state.users.objects.entities (or whatever) then you only need to update the getUser selector and not every place in your app where you were making a reference to the old location.
That makes them particularly handy when it comes to refactoring your Redux store.

Selectors are getters for the redux state. Like getters, selectors encapsulate the structure of the state, and are reusable. Selectors can also compute derived properties.
You can write selectors, such as the ones you saw in redux-saga. For example:
const getUsersNumber = ({ users }) => users.length;
const getUsersIds = ({ users }) => users.map(({ id }) => id);
etc...
You can also use reselect, which is a simple “selector” library for Redux, that memoize selectors to make them more efficient.

Selectors are functions that take Redux state as an argument and return some data to pass to the component.
const getUserData = state => state.user.data;
Why should it be used?
One of the main reasons is to avoid duplicated data in Redux.
Your data object shape keeps varying as your application grows, so rather than making changes in all the related component.It is much recommended/easier to change the data at one place.
Selectors should be near reducers because they operate on the same state. It is easier for data to keep in sync.
Using reselect helps to memoize data meaning when the same input is passed to the function, returns the previous result rather than recalculating again.So, this enhances your application performance.

function mapStateToProps (state) {
return {
user: state.user,
}
}
initialState of reducer by user store
const initialState = {
isAdmin:false,
isAuth:false,
access:[1,2,5]
};
class AppComp extends React.Component{
render(){
const {user: { access:access}} = this.props;
const rand = Math.floor(Math.random()*4000)
return (<div>
{`APP ${rand} `}
<input type="button" defaultValue="change auth" onClick={this.onChangeUserAuth} />
<p>TOTAL STATUS COUNT IS {access.length}</p>
</div>)
}
}}
but you can use selector
var getUser = function(state) {
return state.user
}
const getAuthProp = createSelector(
getUser,
(user) => user.access
);
function mapStateToProps (state) {
return {
// user: state.user,
access: getAuthProp(state)
}
}
Main Problem is this component use all user: state.user and any changes in user (etc isAdmin ,isAuth, access) runs rerender this component which need only part of this store - access!!!
In Redux, whenever an action is called anywhere in the application,
all mounted & connected components call their mapStateToProps
function. This is why Reselect is awesome. It will just return the
memoized result if nothing has changed.
In the real world, you will most likely need the same certain part of
your state object in multiple components.
https://medium.com/#parkerdan/react-reselect-and-redux-b34017f8194c
The createSelector function provided by Reselect implements the most basic way to derive a selector from previous selectors. The simplest use case is to derive a selector from a single other selector. In this case, the parameters to createSelector are the input selector and a function transforming the result of that selector into the result of the new selector. For example
var getProducts = function(state) {
return state.products
}
import {getProducts} from '../app/selectors'
import {createSelector} from 'reselect'
export const getProductTitles = createSelector(
getProducts,
(products) => products.map((product) => product.get('title'))
)
This is equivalent to (ignoring memoization):
import {getProducts} from '../app/selectors'
export const getProductTitles = (state) => {
return getProducts(state).map((product) => product.get('title'))
}
The createSelector function can combine data from multiple selectors as well as from a single selector. We can pass any number of selectors to createSelector, and their results will be passed to the function passed as the final argument. For a (somewhat contrived) example:
const isInCheckout = createSelector(
getIsShippingPage,
getIsBillingPage,
getIsConfirmationPage,
(isShipping, isBilling, isConfirmation) =>
isShipping || isBilling || isConfirmation
)
is equivalent to
const isInCheckout = (state) => {
return (
getIsShippingPage(state) ||
getIsBilingPage(state) ||
getIsConfirmationPage(state)
)
}
common pattern when writing mapStateToProps functions with selectors is to return an object with each key storing the result of a particular selector. The createStructuredSelector helper function in Reselect lets us write this pattern with the minimum of boilerplate. For example, if we writ
const mapStateToProps = createStructuredSelector({
title: getProductTitle,
price: getProductPrice,
image: getProductImage
})
it is equivalent to
const mapStateToProps = (state) => {
return {
title: getProductTitle(state),
price: getProductPrice(state),
image: getProductImage(state)
}
}
https://docs.mobify.com/progressive-web/0.15.0/guides/reselect/

Related

Using the Context API as a way of mimicking useSelector and useDispatch with redux v5

I'm working on a React project where I'm constrained to using React Redux v5, which doesn't include useDispatch and useSelector.
Nonetheless I really would like to have these hooks (or something like them) available in my app.
Therefore, I've created a wrapper component at the top level of the app which I connect using redux's connect function.
My mapStateToProps and mapDispatchToProps then just look like this:
const mapDispatchToProps = (dispatch: DispatchType) => {
return {
dispatch,
};
};
const mapStateToProps = (state: StateType) => {
return {
state,
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MainLayout);
In my wrapper component, I then pass the dispatch and the state into the value:
<DispatchContext.Provider value={{ state, dispatch }}>
{children}
</DispatchContext.Provider>
Finally, I have a hook that looks like this:
const useSelectAndDispatch = () => {
const context = useContext(DispatchContext);
if (context === null) {
throw new Error("Please use useDispatch within DispatchContext");
}
const { state, dispatch } = context;
function select(selector) {
return selector(state);
}
return { dispatch, select };
};
I then use dispatch and selector in my components via useSelectAndDispatch.
I was wondering if this is an appropriate way to go about this issue, and whether I can expect any performance problems. I am using reselect, and have a good understanding of memoization. I'm just looking for opinions, since I've heard that the redux team held off implementing useDispatch and useSelector for a long time because of performance issues.
Many thanks for any opinions!
This will cause significant peformance problems. Your mapStateToProps is using the entire state object, so every time anything changes in the state, the provider must rerender. And since the provider rerendered with a new value, so too must every component that consumes the context. In short, you will be forcing most of your app to rerender anytime anything changes.
Instead of using mapStateToProps and mapDispatchToProps, i would go back to the actual store object, and build your hooks from that. Somewhere in your app is presumably a line of code that says const store = createStore(/* some options */).
Using that store variable, you can then make some hooks. If i can assume that there's only one store in your app, then the dispatch hook is trivial:
import { store } from 'wherever the store is created'
export const useMyDispatch = () => {
return store.dispatch;
}
And the selector one would be something like this. It uses .subscribe to be notified when something in the store changes, and then it uses the selector to pluck out the part of the state that you care about. If nothing changed, then the old state and new state will pass an === check, and react skips rendering. If it has changed though, the component renders (only the component that called useMySelect plus its children, not the entire app)
export const useMySelector = (selector) => {
const [value, setValue] = useState(() => {
return selector(store.getState());
});
useEffect(() => {
const unsubscribe = store.subscribe(() => {
const newValue = selector(store.getState());
setValue(newValue);
});
return unsubscribe;
}, []);
return value;
}

How can I dispatch a change in the constructor?

I am trying to learn redux. I think I have reducers down pretty well, I can pull the data from the store and set it via props.
But I cannot wrap my head around actions and changing data on the state of the store.
I have this:
const mapDispatchToProps = (dispatch) => {
return{
what goes in here?
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ComponentName);
I need to know how to dispatch a change inside the const. just a simple add text to an empty state. example: the state is apples:'', and I want to add 'red delicious' to that.
mapDispatchToProps provides you a way to connect your action creators to your components. Let's assume you have an action creator which increments a counter state
export const change = value =>({
type: 'CHANGE_FRUIT',
fruit : value
})
And you want value to be passed from one of your components. First use connect HOC in this component like you're already doing. Now you need to import incrementCounter from your actions folder
import { change as changeFruit } from './actions/fruit'
Now use mapDispatchToProps like this
const mapDispatchToProps = dispatch =>({
change : fruit => dispatch(changeFruit(fruit))
})
Now you have an action creator serialized inside your component's props and when you call props.increment(2) this will be the equivalent to call
dispatch(changeFruit('apple'))
Here is why you should always do props.increment instead of directly call dispatch inside your component.
So the full implementation inside your component could be like this
import { change as changeFruit } from './actions/fruit'
class Component extends React.component{
render(){
const { fruit, change } = this.props
return(
<>
<div>{fruit}</div>
<button onClick={() => change('orange')}>Change to orange</button>
</>
)
}
}
const mapStateToProps = state =>({
fruit : state.fruit
})
const mapDispatchToProps = dispatch =>({
change : fruit => dispatch(changeFruit(fruit))
})
export default connect(mapStateToProps, mapDispatchToProps)(Component)
Your reducer should look like this
const initialState = {
fruit: 'apple'
}
export const reducer = (state = initialState, action) =>{
switch(action.type){
case 'CHANGE_FRUIT' : return{
...state,
fruit : action.fruit
}
default : return state
}
}
An "action" is just a plain object that describes some sort of change you want to happen in your store. So in your case you want to add text to your string, such an action might look like:
{
type: 'ADD_TEXT',
value: 'red delicious'
}
An action creator is nothing more than a function that returns such a plain object. Notice we can generalise the action creator, so you can pass in the string to add, rather than have it hard coded as 'red delicious'.
const addText = value => ({ type: 'ADD_TEXT', value })
In order to 'send' this action to the reducers, it needs to be passed to dispatch function that Redux provides. E.g. this would dispatch the action:
const action = addText('red delicious') // Create the plain action object
dispatch(action) // Send it to the reducers
That can get kind of tedious to write out manually all the time, so mapDispatchToProps helps with that. It's a function you provide to connect that does the above all in one place, leaving the rest of your component code un-cluttered. Redux calls it internally to generate the props for your connected component. So when you do,
const mapDispatchToProps = dispatch => ({
addText: value => {
// Same code as above:
const action = addText('red delicious')
dispatch(action)
}
})
In your component you can call
props.addText('red delicious')
look when use the connect function in the app it takes 2 arguments the first is the state current and the second argument is the dispatch that specifies what type of action that will be implemented is the specific action the dispatch depended on the despatch will be called the specific action that link by reducer that implements in provider in-store when use
<Provider store={store}>
which the reducer that created by the const store=createStore(rootReducer)
const mapDispatchToProps = (dispatch) => {
return{
example look this is dispatch that well happened it will call specif action bu use key type
this == store.dispatch(constants.WIDITHROUP)
the first is key or name of the handle when you want to implement :(input that enters fro this dispatch and=> it call store.dispatch(name of Handel) )
widthroup: (balance) => dispatch({ type: constants.WIDITHROUP ,balance}),
deposit: (balance) => dispatch({ type: constants.DEPOSIT,balance }) }
}
}
Basically I am answering for the part where you have confusion:
return {
what goes in here?
}
Ideally, if you think as class concept, let's say you have a class that facilitates to choose and save your favorite color selection.
So, all you want is to create a class component and define a method saveSelection that should save selection to store. And, who ever wants to get that selection or see your selected colors they can access from store.
Back to Redux, the rule says, you can not update the state of store directly. You have to dispatch an action.
So, if I already have a decided to create a method saveSelection for that purpose, what should I do, so that it should dispatch an action.
May be something like:
class ColorSelector {
saveSelection () {
dispatch(action); //dispatch is some way to dispatch an action
}
}
But, action is not just a command or text or a value, we need to specify, the type of action (because reducer need to identify which data it need to update based on action) and the data I want to save. That will make sense of an action.
So, I want something like:
//I pass my selected color to saveSelection method and that should save
saveSelection(dataToUpdate) {
dispatch({type: actionType, data: dataToUpdate})
}
One thing may be going inside your head -> How will I get dispatch and how did I just use it here?
That will get clear in a sec, but first lets replace the dispatch arguments which we can write like:
saveSelectionAction(dataToUpdate) {
return {type: actionType, data: dataToUpdate}
}
And our method would be like:
saveSelection(dataToUpdate) {
dispatch(saveSelectionAction(dataToUpdate))
}
And saveSelectionAction becomes the action creator part. Now comes the dispatch part. So, lets wrap this method with another method which provides dispatch and expose saveSelection
anotherMethodWhichPassesDispatch(dispatch) {
// unleash the power of closure
return {
saveSelection: saveSelection(dataToUpdate) {
dispatch(saveSelectionAction(dataToUpdate))
}
}
}
or more correctly
// Method to map dispatching action to components props
anotherMethodWhichPassesDispatch(dispatch) {
// return list of your created action you want as props
return {
// Name it whatever you want and that will be a void method passing data
saveSelection: (dataToUpdate) => {
// pass the action creator's result which is defined above
// Or, exported from a separate file
dispatch(saveSelectionAction(dataToUpdate))
}
}
}
Now pass this method as argument to connect HOC and that will map saveSelection to a props and provide dispatch to your method.
connect(mapStateToProps, anotherMethodWhichPassesDispatch)(<container>) or we can rename it as mapDispatchToProps.
Now go back to your class's saveSelection method and do like:
saveSelection = (selectedColor) => {
this.props.saveSelection(selectedColor);
}
That's it.

React-Redux: Add custom third parameter to mapStateToProps - possibly connectAdvanced?

I have an application using React and Redux.
Several parts of my data model are potientially lazy, so a common pattern when reading data from the redux state is: Lookup if lazy data is already loaded, if not, dispatch a ReduxThunk-action that fetches the data.
To be able to trigger this process within mapStateToProps I want to implement an own connect function that passes a resolve function as third parameter to mapStateToProps. This resolve function should have access to dispatch, so I could trigger the ReduxThunk-action loading of the lazy data. My mapStateToProps function should look like this:
const mapStateToProps = (state, ownProps, resolve) => {
var myData;
if (state.potentiallyLazy instanceof LazyLink) {
resolve(state.potentiallyLazy);
}
else {
myData = state.potentiallyLazy;
}
return {
myData
}
}
Any help how to achieve that, or a hint for a different approach? I had a look at connectAdvanced(), but didn't really find a solution with that.
This is a bad idea on a couple levels.
First, your mapState functions should be pure, synchronous, and have no side effects, same as your reducers.
Second, it looks like you're keeping class instances in your store state, which is discouraged because it will break time-travel debugging.
If you need to trigger loading behavior, I would suggest putting it inside the component, in the lifecycle methods.
You can extend the connect HOC to add your resolve function like
export connectAdvanced = (...args) => {
const resolve = () => {} // your resolve function here
const [mapStateToProps] = args;
const overridenMapStateToProps = (state, ownProps) => {
mapStateToProps(state, ownProps, resolve)
}
const restParams = [overridenMapStateToProps, ...args.slice(1)];
return (Component) => connect.apply(null, restParms)(Component);
}
and use it like
const mapStateToProps = (state, ownProps, resolve) => {
var myData;
if (state.potentiallyLazy instanceof LazyLink) {
resolve(state.potentiallyLazy);
}
else {
myData = state.potentiallyLazy;
}
return {
myData
}
}
export default connectAdvanced(mapStateToProps)(App);
However this approach no longer keeps your mapStateToProps function pure and you should consider moving the lazy loading action in the lifecycle methods, which is a better and correct way to do this

Advantage of Reselect over container component

https://redux.js.org/recipes/computing-derived-data describes how to use reselect createSelector() to avoid unnecessary updates when a component depends on properties that are computed values.
An alternative seems to be to move the necessary computations into a (hopefully) lightweight container component that is connected to the store instead. In this case mapStateToProps() would return references to the store and connect() would not trigger an update of the container component unless the relevant values in the store has changed:
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
const mapStateToProps = state => {
return {
visibilityFilter: state.visibilityFilter,
todos: state.todos,
}
}
const mapDispatchToProps = dispatch => {
return {
onTodoClick: id => {
dispatch(toggleTodo(id))
}
}
}
const connected = connect(
mapStateToProps,
mapDispatchToProps
)
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}
const VisibleTodoList = connected((props) => {
return <TodoList
todos={getVisibleTodos(props.todos, props.visibilityFilter)}
onTodoClick={props.onTodoClick}
/>
})
export default VisibleTodoList
Are there any reasons for preferring reselect over a connected container component? I don't find (m)any examples discussing the above approach.
What if you need to use getVisibleTodos in more than one container?
Selectors decouple your containers from the store. When the store shape changes, you only need to update selectors to get the containers in working order.
For this reason, some would advocate placing selectors alongside the reducers.
Selectors are also useful during testing, allowing you to make assertions in terms of domain logic rather than storage logic. This makes tests less brittle.
It's useful to think of the selectors and actions as two halves of your public store API, and everything else as implementation details that are prone to change.
Also, for many simple cases (like your filtering example) you might want to choose a more humble memoizer function over the more ceremonious setup for createSelector():
const selectVisibleTodos = memoize(getVisibleTodos)
const mapStateToProps = state => ({
todos: selectVisibleTodos(state.todos, state.visibilityFilter),
})
vs
const selectVisibleTodos = createSelector(
(state) => state.todos,
(state) => state.visibilityFilter,
getVisibleTodos
)
const mapStateToProps = state => ({
todos: selectVisibleTodos(state),
})
Pros:
More concise code
Faster than createSelector (Less internal overhead)
Cons:
Less separation between mapSateToProps and the shape of the redux state
I personally tend to keep both createSelector and a memoize function at hand, and choose between them on a case by case basis.

Redux, Reselect and ImmutableJS causing unnecessary renders on child components

Based on all the Redux and Reselect docs I have just read and re-read the below selector should only do the thing.toJS() processing if the Immutable Map that getThing() returns is not equal to the previous one.
...
// Selector
import { createSelector } from 'reselect'
const getThing = (state, thingId) => {
return state.entities.getIn(['things', thingId])
}
const makeThingSelector = () => {
return createSelector(
[getThing],
(thing) => {
return thing.toJS()
}
)
}
export default makeThingSelector
...
// Container
const makeMapStateToProps = () => {
return (state, ownProps) => {
const { thingId } = ownProps
const things = select.makeThingsSelector()(state, thingId)
return {
hasNoThings: things.length === 0,
things
}
}
}
const Container = connect(
makeMapStateToProps,
mapDispatchToProps
)(Component)
...
This holds true unless I have a child 'smart' component. In this case, when the parent triggers a render, the selector called in the child component's container always processes the value regardless of whether the result is new or not.
I have been trying to encapsulate the ImmutableJS API inside my selectors but this means to avoid a re render on these nested components every time their parents update I have to do a deep equality check in the shouldComponentUpdate function. This is expensive and doesn't seem like a decent solution.
The app state is normalised so the updated part of the state tree is not a hierarchical parent to the part of the state that the child component is dependent on.
Am I missing something key here?
On every store update react-redux performs following steps (putting all internal complexities aside):
Calls mapStateToProps and mapDispatchToProps.
Shallowly comparing resulted props
Re-renders Component In case new props differs from previous one.
This way mapStateToProps will be called on every store update by-design. So will following line of code:
...
const things = select.makeThingsSelector()(state, visitId)
...
As you can see new reselect selector will be created every time effectively preventing any memoization (there are no global state in reselect, memoization happens per selector).
What you have to do is change your code so that one and the same selector will be used on every invocation of mapStateToProps:
const thingSelector = select.makeThingsSelector();
...
const makeMapStateToProps = () => {
return (state, ownProps) => {
const { visitId } = ownProps
const things = thingSelector(state, visitId)
return {
hasNoThings: things.length === 0,
things
}
}
}
UPDATE: Also I don't see any reason to use factory-style makeThingsSelector and makeMapStateToProps. Why not just go with something like:
...
// Selector
export default createSelector(
[getThing],
(thing) => thing.toJS()
);
...
// Container
const mapStateToProps = (state, ownProps) => {
const { visitId } = ownProps
const things = select.thingsSelector(state, visitId)
return {
hasNoThings: things.length === 0,
things
}
}
const Container = connect(
mapStateToProps,
mapDispatchToProps
)(Component)
...
Since the redux state in this application uses the ImmutableJS data structure, Reselect may not be necessary.
Firstly, ImmutableJS manipulates only the slice of the data structure affected by a change operation and therefore all changes to the larger state may not affect the slice being passed to the container.
Secondly, the redux connect function returns a pure container by default and upon encountering same slice will not re-render. However, the mapStateToProps will be invoked since the whole state and possibly the ownProps have changed.
For finer control, the rendering of same container can be linked directly to changes to a particular slice of the state and ownProps by adding areStatesEqual and areOwnPropsEqual predicate properties to the fourth parameter of the connect function (better known as the options object).
const mapStateToProps = ({ entities }, { thingId }) => {
const things = entities.getIn(['things', thingId]).toJS();
return {
hasNoThings: things.length === 0,
things
};
};
const Container = connect(
mapStateToProps,
mapDispatchToProps,
undefined, {
areOwnPropsEqual: (np, pp) => np.thingId === pp.thingId,
areStatesEqual: (ns, ps) => ns.entities.get(‘things’).equals(
ps.entities.get(‘things’)
)
}
)(Component);
If both of these predicates are true, not only would the container and its children not re-render, the mapStateToProps would not even be invoked!

Resources