Composing a redux reducer for nested objects in an array - reactjs

I'm new to Redux and am having some difficulty composing a working reducer for my situation.
My current state looks like this
export const userData = {
userID: '12345678',
userDetails: {
firstName: 'Joe',
surname: 'Bloggs'
},
currentGames: [
{
gameId: 'G-00000001',
gameSelections: [
{
subgameId: '',
selection: ''
}
]
}
]
};
My action looks like this
function selectWinner (gameId, subgameId, selection) {
return {
type: SELECT_WINNER,
gameId,
subgameId,
selection
}
}
The aim is to be able to add/update the objects in the gameSelections array.
There may be more than one Object in the currentGames array also.
I've heard I should use .map but I'm not really sure how.

You're on the right track for using .map() to iterate over the array of objects. It also looks like your action-creator has all the necessary parameters to update your reducer state.
Your reducer can look something like this:
const userReducer = (state=userData, action) => {
switch(action.type){
case SELECT_WINNER:
return {
...state,
currentGames: [...state.currentGames].map((game) => {
if(game.gameId == action.gameId){
return {
...game,
gameSelections: [...game.gameSelections].map((gameSelection) => {
if(gameSelection.subgameId == action.subgameId){
return {
...gameSelection,
selection: action.selection
}
} else {
return gameSelection
}
})
}
} else {
return game
}
})
}
default:
return state
}
}
Kind of messy, but would get the job-done with a deeply nested state.

Add item to array:
case'ADD_ITEM':
return {
...state,
some_arr: [...state.some_arr, action.payload]
}
update spicific item in array:
case 'UPDATE_ITEM':
return {
...state,
some_arr: state. some_arr.map(
(item, index) => index === specific_index
? {...item, ...action.payload}
: content
)
}

Deep cloning of the state is required.
useful link-https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns
const reducer = (state = userData, action) => {
switch (action.type) {
case CASENAME:
return {
userID: state.userID,
userDetails: {
...state.userdetails
},
currentGames: [
{
gameId: action.gameId,
gameSelections: [
{
subgameId: action.subgameId,
selection: action.selection
}
]
}
]
};
}
}

Related

How to disable user from ordering from more than one restaurant

I'm creating a react native food ordering application. I want to disable the user from ordering from multiple restaurants at the same time. Currently, with using Redux, the user can choose multiple dishes from multiple restaurants. I'm not sure what to search for to find a solution.
For example, in some apps the function/message appears
"Start new order? Your basket with Restaurant X will be cleared."
This is my code for setting up redux reducer.
let defaultState = {
selectedItems: { items: [], cafeName: "" },
};
let cartReducer = (state = defaultState, action) => {
switch (action.type) {
case "ADD_TO_CART": {
let newState = { ...state };
if (action.payload.checkboxValue) {
console.log("ADD TO CART");
newState.selectedItems = {
items: [...newState.selectedItems.items, action.payload],
cafeName: action.payload.cafeName,
};
} else {
console.log("REMOVE FROM CART");
newState.selectedItems = {
items: [
...newState.selectedItems.items.filter(
(item) => item.name !== action.payload.name
),
],
cafeName: action.payload.cafeName,
};
}
console.log(newState);
return newState;
}
default:
return state;
}
};

how to update a specific value in an object in redux

My Reducer :
function updatePersonDetailsReducer (state = {
updatePersonDetails: {
name: "John",
id: "3ery4",
address: "somewhere on earth"
}
}, action) {
switch (action.type) {
case C.UPDATE_PERSON_DETAILS: {
return {
...state
,updatePersonDetails :action.payload
}
}
default : {}
}
return state
}
I called this way in my component,
this.props.dispatch(updatePersonDetails({name:Variant.objValue.name,id:Variant.objValue.Id}))
But this seems to, create a new array than overwriting the values in the current array (updatePersonDetails) in reducer.
You can spread your object and do it like this.
case C.UPDATE_PERSON_DETAILS: {
return {
...state,
updatePersonDetails :
{...state.updatePersonDetails,...action.payload}
}
}
This will keep your old keys there and will only update the newly added keys.

Reducer doesn't update state

I am trying to create a reducer to update a property in object, but i cant be able to update and store the new state information
Reducer
export default function hideCardNumber(state = INITIAL_STATE, action: Action) {
if (action.type === 'HIDE_CARDNUMBER') {
return {
...state,
data: {...state.data, action }}
}
else
return state
}
Action
export const toggleViewNumberCard = (cardId: number, hideCardNumber: boolean) => {
return {
type: 'HIDE_CARDNUMBER',
cardId,
hideCardNumber,
}
}
dispatch to action
function handleToggleViewCardNumber() {
cards.map((card: Card) => {
if (card.cardId === props.activeCard ) {
dispatch(toggleViewNumberCard(
card.cardId,
!card.hideCardNumber,
))
}
})
}
Initial State
export const INITIAL_STATE = {
activeCard: 0,
data: [
{
cardId: 0,
cardName: 'Card',
cardUsername: 'Name',
cardNumber: '1234 1234 1234 1234',
hideCardNumber: false, <-- Trying to replace this property when reducer update
},
]
}
You need to update reducer like this:
const {hideCardNumber, cardId} = action;
return {
...state,
data: state.data.map(item => item.cardId === cardId ? {...item, hideCardNumber} : item )
}
In real-world scenarios, cardID will be a hash. Also to easily maintain the store data when the application grows INITIAL_STATE should be like this.
export const INITIAL_STATE = {
activeCard: 0,
data: {
123456: {
cardId: 123456,
cardName: 'Card',
cardUsername: 'Name',
cardNumber: '1234 1234 1234 1234',
hideCardNumber: false,
},
}
}
Then the Reducer will be like this.
export default function hideCardNumber(state = INITIAL_STATE, action) {
if (action.type === 'HIDE_CARDNUMBER') {
return {
...state,
data: {
...state.data,
[action.cardId]: {
...state.data[action.cardId],
hideCardNumber: action.hideCardNumber
}
}
}
}
else
return state
}
If the activeCard matches one of the IDs in the cards, the code will work perfectly.
first of all it's better to put you data in payload like:
export const toggleViewNumberCard = (cardId: number, hideCardNumber: boolean) => {
return {
type: 'HIDE_CARDNUMBER',
payload : {
cardId,
hideCardNumber,
}
}
}
looks like you have array of cards ,first of all you must have find your current card that you wanna replace using cardid like this:
const index = state.data.findIndex(
(card) => cardId === action.payload.cardId
);
then copy your old array :
const newArray = [...state.data];
then replace that index of newarray with your new hideCardNumber value like this:
newArray[index] = {
...newArray[index],
action.payload.hideCardNumber
};
return {
...state,
data: newArray,
};
i hope it would help you

Redux reducers adding a value to complex multidimensional object

i have an object in my initialState in redux reducer.The object is multidimesional.the object is like below
tradings: {
'buy': {
data: []
},
'sell': {
data: []
},
'total': {
data: []
}
},
So when i want to reduce an action payload (i.e: '1212') how i can push item into data array?
for example:
case 'EXCHANGE_BUY' : {
return{
...state,
tradings: state.tradings['buy'].data.concat(action.payload)
}
}
But it only returns an array like this: tradings: ['1212'] How can i get like this>
tradings: {
'buy': {
data: ['1212']
},
'sell': {
data: []
},
'total': {
data: []
}
},
You need to make sure you clone every level of your redux reducers state. You can do it like this:
case 'EXCHANGE_BUY' : {
return{
...state,
tradings: {
...state.tradings,
buy: {
data: state.tradings.buy.data.concat(action.payload)
}
}
}
}
case 'EXCHANGE_BUY':
return {
...state,
tradings: {
...state.tradings,
buy: {
data: {
state.tradings.buy.data.concat(action.payload)
}
}
}
}
Or the second approach you can use is Immutability helpers.
Thanks.
Use the spread operator to clone until the level is reached:
case 'EXCHANGE_BUY' : {
return{
...state,
tradings: {
...state.tradings,
buy : {
data: [...state.tradings.buy.data, action.payload]
}
}
}
}

How to assign new key value pair without overwriting top level keys?

How do I add a key/value pair to a nested object without overwriting the top level keys in my Redux reducer?
notes state before:
notes: {
-KQpqwDTyLOzd-8UXi-z: {
created: 1473035305252,
text: 'stuff',
}
-KQqe4xiwV4-5WIs2Gpg: {
created: 1473017044898,
text: 'more stuff',
}
}
notes state after:
notes: {
0: {
created: 1473035305252,
text: 'stuff',
new: 'new value',
}
1: {
created: 1473017044898,
text: 'more stuff',
}
}
here is my reducer that is producing the above results:
import _ from 'lodash'
const notes = (state = [], action) => {
switch (action.type) {
case 'FETCH_NOTES':
return action.payload;
case 'CLEAR_NOTES':
return state = [];
case 'UPDATE_NOTE':
console.log(state)
return _.map(state, (note, index) => {
if (index === action.id) {
return _.assign({}, note, {
new: action.new
})
}
return note
})
default:
return state
}
}
export default notes
Please use mapValues function instead of map function. Updated code below.
return _.mapValues(state, (note, index) => {
if (index === action.id) {
return _.assign({}, note, {
new: action.new
})
}
return note
})

Resources