Is this redux reducer OK - javascript

Is this reducer OK:
function someReducer(state = initialState, action) {
if (action.type === SOME_ACTION) {
const newState = Object.assign( {}, state );
// ...
// doing whatever I want with newState
// ...
return newState;
}
return state;
}
and if is OK, why we need all those immutable libraries to complicate our lives.
p.s
Just trying to comprehend Redux and immutability

export default function (state = initialState, action) {
const actions = {
SOME_ACTION: () => {
return {
...state
}
},
ANOTHER_ACTION: () => {
return {
...state
error: action.error
}
},
DEFAULT: () => state;
}
return actions[action.type] ? actions[action.type]() : actions.DEFAULT();
}
I prefer doing this instead. I am not a big fan of switch statements.

The standard approach is to use a switch/case with spread syntax (...) in your reducer.
export default function (state = initialState, action) {
switch (action.type) {
case constants.SOME_ACTION:
return {
...state,
newProperty: action.newProperty
};
case constants.ERROR_ACTION:
return {
...state,
error: action.error
};
case constants.MORE_DEEP_ACTION:
return {
...state,
users: {
...state.users,
user1: action.users.user1
}
};
default:
return {
...state
}
}
}
You can then use ES6 spread syntax to return your old state with whatever new properties you want changed/added to it.
You can read more about this approach here...
https://redux.js.org/recipes/using-object-spread-operator

I found something that I really like:
import createReducer from 'redux-starter-kit';
const someReducer = createReducer( initialState, {
SOME_ACTION: (state) => { /* doing whatever I want with this local State */ },
SOME_ANOTHER_ACTION: (state) => { /* doing whatever I want with local State */ },
THIRD_ACTION: (state, action) => { ... },
});

If your state has nested objects or arrays, Object.assign or ... will copy references to your older state variable and it may cause some issue. This is the reason why some developers use immutable libraries as in most of the case state has deep nested array or objects.
function someReducer(state = initialState, action) {
if (action.type === SOME_ACTION) {
const newState = Object.assign( {}, state );
// newState can still have references to your older state values if they are array or orobjects
return newState;
}
return state;
}

Related

How to save an object in redux?

I build an app in React with Redux and I try to send to my state an object and I try to save it in 'thisUser' but I don't know how to write that 'return' because mine doesn't work.
My Redux state:
const initialState = {
thisUser: {}
}
export function usersReducer(state = initialState, action) {
switch (action.type) {
case 'users/addUser':
return { ...state, thisUser: { ...state.thisUser, ...action.payload} } //the problem
default:
return state
}
}
Dispatch method:
dispatch({ type: "users/addUser", payload: new_user });
Can you tell me how to write that return, please?
If you want to append new user then why are you using object type. You should use Array Type thisUser.
const initialState = {
thisUser: []
}
export function usersReducer(state = initialState, action) {
switch (action.type) {
case 'users/addUser':
return { ...state, thisUser: [ ...state.thisUser,action.payload ] }
default:
return state
}
}
Or
If you want to save only single user object then change only that line in your code:
return { ...state, thisUser: action.payload }
It's better to use an array type for if you have a list of users .
If you have a case when you need to use an object just change the brackets [ ] on my code to curly braces { } .
const initialState = {
thisUser: [],
}
export function usersReducer(state = initialState, action) {
switch (action.type) {
case 'users/addUser':
return { ...state, thisUser: [ ...state.thisUser, ...action.payload]}
default:
return state
}
}

Spread array of objects to array of objects react

I am using redux with react and I am trying to append my array of objects gotten from my api to my redux state which is an array of objects
This is my reducer...
import { GET_BOOKS } from "../actions/types";
const initialState = {
books: [
0:{},
1:{},
]
};
export default function(state = initialState, action) {
switch (action.type) {
case GET_BOOKS:
console.log(action.payload.results);
return { ...state };
default:
return state;
}
}
My api is returning
results : [
0: {somevalue},
1: {somevalue}
]
I dont know how to spread the values into a new array.
Simply assign the property and it will overwrite the old one.
export default function(state = initialState, action) {
switch (action.type) {
case GET_BOOKS:
console.log(action.payload.results);
// spread current state and inaddition to that set new books data
// which overwrites books property from old state
return { ...state, books : action.payload.results };
// spread --^^^^^^^^^^^^^^^^^^^^^^^^^^^---
default:
return state;
}
}
UPDATE : If you want to concatenate it with existing then do something like this.
export default function(state = initialState, action) {
switch (action.type) {
case GET_BOOKS:
console.log(action.payload.results);
return { ...state, books : [...state.books, ...action.payload.results] };
default:
return state;
}
}
FYI : The ...state part is for copying other state properties(assumes there exists other state values)
You need to concat current state and data coming from api
return { books: [...state.books, ...action.payload.results] };
Complete Code
import { GET_BOOKS } from "../actions/types";
const initialState = { books: [] };
export default (state: Object = initialState, action: Object) => {
switch (action.type) {
case GET_BOOKS:
return { books: [...state.books, ...action.payload.results] };
default:
return state;
}
};

Update redux state object property

How do I update the property of an object in redux and preserve the rest of the object..
const appReducer = (state = initialState, action) => {
switch (action.type) {
case 'LOGIN_SUCCESS':
return Object.assign({}, state, {
user: { loggedIn: true, level: 'default' },
});
case 'UPDATE_PACKAGE': {
// add a new value for the user.level which would be in action.level
return { ...state, level: action.level };
}
default:
return state;
}
};
So I would expect the UPDATE_PACKAGE to change the contents of the redux store level property... but its unchanged...
So it seems like you're setting level on the root state instead of the user object.
This should fix it:
case "UPDATE_PACKAGE": {
return { ...state, user: { ...state.user, level: action.level } };
}

Redux doesn't re-render the components

I have a component which takes data from mapStateToProps() method. Component's code is:
handleClick = () => {
if (this.props.data.status) {
this.props.changeIpStatus(index, !this.props.data.status);
} else {
this.props.changeIpStatus(index, !this.props.data.status);
}
}
render() {
if (this.props.data.status) {
this.switchClasses = `switcher blocked`;
this.dotClasses = `dot blocked`;
} else {
this.switchClasses = `switcher`;
this.dotClasses = `dot`;
}
return (
<div className="block">
<div onClick={this.handleClick} className={this.switchClasses}>
<div className={this.dotClasses}></div>
</div>
</div>
)
}
}
My Redux connection looks like:
const mapStateToProps = (state) => ({
data: state.ipTable.data.clicks,
})
const mapDispatchToProps = (dispatch) => {
return {
changeIpStatus: (index, status) => {
return dispatch(changeIpStatus(index, status));
},
}
}
export default connect(mapStateToProps, mapDispatchToProps)(BlockSwitcher)
When I click switcher it should re-render because the data is changed. I see that the data is changed through my console log. But it doesn't invoke re-render. Why? My component have mapStateToProps with data that changing and action import is correct (checked).
UPDATE:
This is my reducer:
const initialState = {
data: {}
}
const ipReducer = (state = initialState, action) => {
switch (action.type) {
case `SET_CLICKS`:
return {
...state,
data: action.data
}
case `CHANGE_IP_STATUS`:
let newState = Object.assign({}, state);
newState.data.clicks[action.index].status = action.status;
return newState;
default: return state;
}
}
export default ipReducer;
You can use JSON.parse(JSON.stringify(...)) method but be aware of that if your state includes a non-serializable property then you lose it.
Here is an alternative approach. You can see this method more frequently.
// map the clicks, if index match return the new one with the new status
// if the index does not match just return the current click
const newClicks = state.data.clicks.map((click, index) => {
if (index !== action.index) return click;
return { ...click, status: action.status };
});
// Here, set your new state with your new clicks without mutating the original one
const newState = { ...state, data: { ...state.data, clicks: newClicks } };
return newState;
The second alternative would be like that. Without mapping all the clicks we can use Object.assign for the clicks mutation.
const newClicks = Object.assign([], state.data.clicks, {
[action.index]: { ...state.data.clicks[action.index], status: action.status }
});
const newState = { ...state, data: { ...state.data, clicks: newClicks } };
return newState;
The problem was with deep copy of an object. In JavaScrip for copying object without any reference between them we have to use, for example:
let newState = JSON.parse(JSON.stringify(state));
not this:
let newState = Object.assign({}, state); // <-- this is do not return a standalone new object. Do not use it in your reducer.
Thanks to #kind user!
P.S This is an article with examples why Object.assign() do not work in this case.

redux - how to store and update a key/value pair

I am using redux wth reactjs.
I want to store simple key/value pairs but can't get the reducer syntax right.
In this case each key/value pair will hold a connection to an external system.
Is this the right way to do it? I'm at the beginning with redux so it's a bit of mystery.
export default (state = {}, action) => {
switch(action.type) {
case 'addConnection':
return {
connections: {
...state.connections, {
action.compositeKey: action.connection
}
}
default:
return state
}
}
This worked for me:
export default (state = {}, action) => {
switch(action.type) {
case 'addConnection':
return {
...state,
connections: {
...state.connections,
[action.compositeKey]: action.connection
}
}
default:
return state
}
}
From the docs:
https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns#correct-approach-copying-all-levels-of-nested-data
You just have a couple mistakes with {} instead of [] and forgetting to use Object.assign.
const reducer = (state = {}, action) => {
switch (action.type) {
case 'addConnection':
return Object.assign({}, state, {
connections: [
...state.connections,
{
[actions.compositeKey]: action.connection
}
]
});
default:
return state;
}
}
export default reducer;
It might help to see it expressed this way too. It does the same thing but I think it reads a little nicer
const reducer = (state = {}, {type, compositeKey, connection}) => {
switch (type) {
case 'addConnection':
return Object.assign({}, state, {
connections: state.connections.concat({
[compositeKey]: connection
})
});
default:
return state;
}
}
export default reducer;
Or if you're using Immutable, something like this
import Immutable from 'immutable';
const reducer = (state = Immutable.Map(), {type, compositeKey, connection}) => {
switch (type) {
case 'addConnection':
return state.set(
'connections',
state.get('connections').concat({
[compositeKey]: connection
})
);
default:
return state;
}
}
export default reducer;
This may work
const reducer = (state = {}, {type, compositeKey, connection}) => {
switch (type) {
case 'addConnection':
var newData={};
newData[compositeKey]=connection;
return Object.assign({}, state, newData)
});
default:
return state;
}
}
export default reducer;

Categories

Resources