call to mapStateToProps appears to pass wrong state object - javascript

I am working on my first React-Redux app.
This is the code of my reducer (I only have one):
const CalculationsReducers = (state = {}, action) => {
switch (action.type) {
case CalculationsActions.LOAD_CALCULATIONS:
return Object.assign({}, state, {
calculations: [{
id: 'abc',
name: 'test',
date: 'test',
status: 'in progress'
}]
});
default:
return state;
}
};
This is the code of my 'mapStateToProps' function I use with connect()
const mapStateToProps = (s) => {
return {
calculations: s.calculations ||[]
};
};
When I dispatch an action with type LOAD_CALCULATIONS, I can see a log trace (using react-logger), but the state object seems very strange to me. Can someone point me my error?
log trace using react-logger

The workflow looks good to me. The state object also looks fine, but you might want to change the alias when importing reducer to configure your store, to make it more intuitive.
In your configure store file:
import calculations from 'reducers/CalculationReducer';
Note that it requires you to export CalculationReducer as default. Then you will see calculations instead of CalculationReducer in your state object.
And I noticed you have a payload along with your action but never got handled in reducer. If you need it in state object then handle it in reducer using action.payload.

Related

Actions must be plain objects, even though it seems my actions are already plain

I am developing an app with React Native and Redux. I have some authentication and localisation logic handled by Redux, and now I want to fetch needed data from remote API using Redux as well.
An example of what I had for authentication:
actions.auth.js
export function saveAuthToken(authToken) {
return {
type: "SAVE_AUTH_TOKEN",
authToken
};
}
...
reducers.auth.js
export function authToken(state = "", action) {
switch (action.type) {
case "SAVE_AUTH_TOKEN":
return action.authToken;
...
default:
return state;
}
}
What I am now trying to add:
actions.data.js
export function fetchData() {
return {
type: "FETCH_DATA"
};
}
...
reducers.data.js
export function dataList(state = {}, action) {
switch (action.type) {
case "FETCH_DATA":
return {};
...
default:
return state
}
}
For test purposes, I now don't even make any calls to API. Just try to return {} for data in any case.
Even though I think that I made everything identical to the way I handled authentication (which worked), when I try to call:
store.dispatch('FETCH_DATA');
I get the following error:
Error: Actions must be plain objects. Use custom middleware for async actions.
I don't quite get where exactly I try to use async actions and why my actions aren't plain objects.
Any help is appreciated.
store.dispatch() takes an action as an argument.
And, as the error says, the action must be plain object.
The error is obvious, because
what you are passing to dispatch() is a string and not an object.
Here is what you would want to do:
import { fetchData } from '<file-path-here>'
store.dispatch(fetchData())
You see, the fetchData() would then return a plain object which is required by store.dispatch()
If you are not using any custom middleware then your action must be plain object in your case like below
store.dispatch({
type : 'FETCH_DATA',
payload : {} // if any pass here
})
You can not dispatch action as string it must have a plain object like
{
type: 'YOUR_ACTION',
payload: yourPayload
}
if you want to pass function or else you must use custom middleware it will also use to dispatch function you can use thunk as middleware or redux-saga.
This link will help you.
redux-thunk
redux-saga
It will help you.
reducers.data.js
export function dataList(state = {}, action) {
switch (action.type) {
case "FETCH_DATA":
return {...state};
...
default:
return state
}
}
export function fetchData() { return dispatch{ type: "FETCH_DATA" }; }
use dispatch to dispatch your actions

sonar code smell for reducer used in combineReducer

I am using combineReducer to combine reducers and reducer like this
const todo = (state = {}, action) => {
switch (action.type) {
//...
case 'TOGGLE_TODO':
if (state.id !== action.id) {
return state
}
return Object.assign({}, state, {
completed: !state.completed
})
default:
return state
}
}
My problem is if i am defining reducer like that i am getting sonar code smell
Function parameters with default values should be last1
but combine reducer pass argument in this sequence only how to work on this?
we did have the same issue within our project, and sonar allows you to define exclusions for rules and files in Administration -> Congifuration -> Analysis Scope.
you will find there a section called Ignore issues on Multiple Criteria and there you can enter the rule and a "file pattern" to exclude files from this rule.
like:
From the Sonarqube docs:
Function parameters with default values should be last:
...But all function parameters with default values should be declared
after the function parameters without default values. Otherwise, it
makes it impossible for callers to take advantage of defaults; they
must re-specify the defaulted values or pass undefined in order to
"get to" the non-default parameters.
This does, however, work with Redux as it calls your reducer for the first time with undefined as the first argument. If you want to continue using this pattern, you'll need to disable the rule or skip that line from analysis.
What if you define a default value for the second arg?
const todo = (state = {}, action = null/undefined) => {
Set action with {}:
for example, changing the below code
const todo = (state = {}, action) => { }
to this
const todo = (state = {}, action = {}) => { }
Will be the safest way.

Updating multiple values in redux

I'm trying to update a nested object in redux but I'm not quite sure if I'm doing it correctly. The code provided below doesn't compile as there's a syntax error in the reducer.
I understand it's not the right syntax as there's no key before action.payload but I'm lost in terms of how I merge the action.payload into the [action.id].
I'm trying to avoid creating individual actions for every key I need to update (e.g. updateBlockType, updateBlockTitle, updateBlockDescription, etc.)
Dispatch:
this.props.dispatch(updateBlock(this.props.id, { type: e.currentTarget.id }))
Action:
export function updateBlock (id, values) {
return {
type: 'UPDATE_BLOCK',
id: id,
payload: values
}
}
Reducer:
case 'UPDATE_BLOCK':
return {
...state,
blocks: {
...state.blocks,
byHash: {
...state.blocks.byHash,
[action.id]: {
...state.blocks.byHash[action.id],
action.payload <-- How do I merge the payload?
}
}
}
}
byHash[id] object:
'1': { id: 1, type: 'INTRO', title: 'Some title...', description: 'Some description...' }
My way of structuring my reducer might be wrong, so I'm open to how else I should tackle this.
You missed the spread operator : ...action.payload which will spread the properties of the payload which is an object to the object entry in reducer
If the structure gets more complex, it is better to compose reducers to sub-reducers and merge them with thr combineReducers api from redux.
Also you are accessing state.blocks and state.blocks.hash in the reducer. This might throw an error if initial state does not define these keys. That is one more reason to compose complex reducers.

How to stop reducer key being a key in itself

basically I always have this problem whereby as reducers are keys of the object store
they are accessed like this.props.users for examples
so if users state is an array, this is fine
const initialState = []
but if I have this:
const initialState = {
users: [],
loading: false,
error: ""
};
coz you know, my reducer needs to do a few more things
then suddenly I have this horrible thing all over my codebase where I'm doing things like:
this.props.users.users clearly, disgusting. how can I get it back to this.props.users but then able to access the other stuff as well?
one example of reducer code case:
case FETCHING_USERS_SUCCEEDED: {
return {
...state,
loading: false,
users: [].concat(...state.users).concat(action.userData)
};
}
you can declare user as a variable like this
const {users} = this.props.users
thereafter you can use users instead of this.props.users.users
When you map your state to props, map state.users.users instead of just state.users.

Redux reducer to fetch, like, and dislike posts

I have made a like functionality with Redux and React Native.
I have a reducer with initial state posts = [] and my reducer looks like
const initialState = {
posts: []
};
function postReducer(state = initialState, action) {
switch (action.type) {
case FETCH_POSTS:
return {
...state,
posts: action.posts
};
case LIKE_POST:
return {
...state,
posts: state.posts.map(post => {
if (post._id === action.postId) {
return {
...post,
likes: !!post.likes ? post.likes.concat(action.userId) : [action.userId]
}
}
return post;
})
};
case UNLIKE_POST:
return {
...state,
posts: state.posts.map(post => {
if (post._id === action.postId) {
return {
...post,
likes: post.likes.filter(userId => userId !== action.userId)
}
}
return post;
})
};
default:
return state
}
};
I know that I can not mutate the posts array in my state, so I have to return a new array of posts where I have modified the post that a user tries to like/unlike.
It seems to work very well, but it's daunting slow. I only have very few posts, but I still have to wait almost a second for it the like to be visible.
Is this the right approach? Am I storing my posts correctly as a simple array in the state? I'm not sure what the alternative is, but I have, for instance, seen in this GitHub repo that it can be done different, although I don't fully understand the structure of it.
You are right that this is slow and tedious, but it is necessary if you want to ensure you don't mutate state and stick to the correct patterns that are suggested.
That said you have a few options, an obvious one is to make helper functions that solve the common problems you face, however a (personally) better solution is to use ImmutableJS. It will provide you with three things:
The first is it guarantees you aren't mutating state as all changes return a new and different copy.
The second is a convenient API that will allow you to do your mutations with ease instead of using awkward syntax to work around vanilla javascript.
The third is more of a minor benefit but you get access to Immutable types such as List, Map, a variety of Collections and define your own types with Records, which I recomment to use for each reducer state as you can declare the shape of the state, and only mutate the defined properties, instead of adding new ones.
With Immutable a single part of your reducer would look like this:
case FETCH_POSTS:
return state.set('posts', action.posts);
case LIKE_POST:
let posts = state.posts.map( (p) => {
if (p.id === action.postId) {
return p.set('likes', 'something');
}
return p;
}
It gets even more exciting if you build your state off a Record, so you would have:
export default Record({
posts: List(),
}, 'SocialRecord');
Which would then let you have state.get('posts') or state.posts and still have immutable functionality as Records have getters.
Check out the docs for more information:
Immutable Documentation

Categories