I've started learning selectors and redux. The state of the app looks as follows:
And then unfolded:
I'm trying to add a selector that would count the total of the sub-array first elements eg(6 + 4 + ..., etc), but first things first:
I've started writing a selector (for now just to display anything):
import { createSelector } from 'reselect';
const getValues = (state) => state.grid;
export const getSelected = createSelector(
[getValues],
grid => grid[0]
);
Then a container has:
const mapStateToProps = state => ({
score: state.score,
grid: state.grid,
values: getSelected(state.grid)
});
But I get an error: Cannot read property '0' of undefined.
The whole code can be seen here:
https://github.com/wastelandtime/memgame
Please advise. Thank you
You are passing wrong value to your selector. Change values: getSelected(state.grid) to values: getSelected(state) in your mapStateToProps.
Related
Is there a way to make useState not to share the state between multiple instances of the same component without hacking the key argument with Math.random().
const activated = useState("activated", () => false)
I'd suggest to use useState if you want to share state. If you don't want to, you could use const activated = ref(false). You must not use it outside the setup function though.
const activated = () => useState(false) would be another option (see https://v3.nuxtjs.org/guide/features/state-management).
I'm new to front-end development, so maybe someone could kindly clarify the following:
In Redux, I see in all the video tutorials that actions are defined like so:
const SOME_ACTION = "SOME_ACTION"
export const someAction = () => ({
type: SOME_ACTION
})
I wonder, what's the point of defining the SOME_ACTION constant?
Why not just skip it and name the action in the function itself? For instance:
export const someAction = () => ({
type: "SOME_ACTION"
})
What are we gaining by having a global variable that is only used within a function by the same name?
Many thanks!
I am new to react native with redux. I am trying to figure out how all the pieces in react-native redux integration. The one thing giving me trouble is understanding the difference types and selector give me more details.
MapStateToProps -> has his name say, you can map state objects to props. Example:
You have a store like this:
{
name:'paul',
surname:'watson'
}
Then you need show in your component the name, so in your container you can access to this data stored in store with mapstatetoprops, like this:
const mapStateToProps = (state, ownProps) => ({
myname: state.name,
})
MapDispatchToProps -> thats when you need dispatch an action, you map an action to a prop to you can use in your component
You have an action like:
const setMyName = payload => ({
type: SET_MY_NAME,
payload,
})
then you need update your name in store when user click something throw this action, so you can map this action in a prop to call like updateName('pepito') with mapDispatchToProps, like this:
const mapDispatchToProps = {
updateName: setMyName,
}
Selectors -> it's just an abstraction code, selectors make your life more easy.
Selectors are functions that take Redux state as an argument and return some data to pass to the component, like this:
const getDataType = state => state.editor.dataType;
Thats a basic concepts, you should read oficial document and search, in internet have a lot of articles about this.
What does the following code snippet do? It is taken from this file.
export const getCollectionLoading = createSelector(getCollectionState, fromCollection.getLoading);
The fromCollection.getLoading has only either true or false value, so can there be any optimization achieved by using the createSelector?
in
export const getCollectionLoaded = createSelector(getCollectionState, fromCollection.getLoaded);
export const getCollectionLoading = createSelector(getCollectionState, fromCollection.getLoading);
export const getCollectionBookIds = createSelector(getCollectionState, fromCollection.getIds);
According to the example app's comments:
/**
* Every reducer module exports selector functions, however child reducers
* have no knowledge of the overall state tree. To make them useable, we
* need to make new selectors that wrap them.
**/
For example in reducers/collections.ts the selectors at the bottom of the file only reference the collection slice:
export const getLoaded = (state: State) => state.loaded;
export const getLoading = (state: State) => state.loading;
export const getIds = (state: State) => state.ids;
These collection selectors can't be used with state.select() because state.select expects to work with the whole AppState. Therefore in the reducers/index.ts the selector is then wrapped with another selector so that it does work with the whole AppState:
export const getCollectionLoading = createSelector(getCollectionState, fromCollection.getLoading);
Back to your question: why is it necessary to use reselect for this. Reselect in this case isn't providing any optimizations. So the main purpose of reselect is that it provides the ability to compose selectors together.
By using this composition feature we can make collections.ts only have selectors for the collections slice. Then in index.ts we can have selectors at the AppState level. Furthermore we can have selectors that work across different slices of the store such as this one:
/**
* Some selector functions create joins across parts of state. This selector
* composes the search result IDs to return an array of books in the store.
*/
export const getSearchResults = createSelector(getBookEntities,
getSearchBookIds, (books, searchIds) => {
return searchIds.map(id => books[id]);
});
Yes, there may be performance gains. If fromCollection.getLoading computation is expensive, reselect would avoid useless recalculations until getCollectionState keeps returning the same value.
I have 2 reducers that are combined in a Root Reducer, and used in a store.
First reducer 'AllTracksReducer" is supposed to return an object and the second 'FavoritesReducer' an array.
When I create a container component and a mapStateToProps method in connect, for some reason the returned state of the store is an object with 2 reducer objects which hold data, and not just an object containing correposding data, as expected.
function mapStateToProps(state) {
debugger:
console.dir(state)
//state shows as an object with 2 properties, AllTracksReducer and FavoritesReducer.
return {
data: state.AllTracksReducer.data,
isLoading: state.AllTracksReducer.isLoading
}
}
export default connect(mapStateToProps)(AllTracksContainer);
so, in mapStateToProps, to get to the right state property, i have to say
state.AllTracksReducer.data... But I was expecting the data to be available directly on the state object?
Yep, this is a common semi-mistake. It's because you're using likely using ES6 object literal shorthand syntax to create the object you pass to combineReducers, so the names of the imported variables are also being used to define the state slice names.
This issue is explained in the Redux docs, at Structuring Reducers - Using combineReducers.
Create some selectors that receive the whole state (or the reducer-specific state) and use it in your mapStateToProps function. Indeed the name you define when you combineReducers will be the topmost state keys, so your selectors should take that into account:
const getTracks = (state) => state.allTracks.data
const isLoading = state => state.allTracks.isLoading
This assumes you combine your reducers with allTracks as they key like here:
combineReducers({
allTracks: allTracksReducer
})
And then you can use those selectors in your mapper, like
const mapStateToProps = state => ({
isLoading: isLoading(state),
tracks: getTracks(state)
})
There's a delicate link between your combineReducers call and your selectors. If you change the state key name you'll have to update your selectors accordingly.
It helps me to think of action creators as "setters" and selectors as "getters", with the reducer function being simply the persistence part. You call your setters (dispatching action creators) when you want to modify your state, and use your selectors as shown to get the current state and pass it as props to your components.
Well, that's how it supposed to work. When you're using combineReducers, you're literally mapping the name of a reducer to the reducer function.
If it bothers you, I would suggest a little syntactical magic if you're using es2016 (though it seems you're not) like so:
function mapStateToProps(state) {
const { data, isLoading } = state.allTracksReducer;
return {
data: data,
isLoading: isLoading
}
}
export default connect(mapStateToProps)(AllTracksContainer);
Remember, state is the one source of truth that possesses all your reducers.