How do i write this inside of an reducer to change the state?
doc = {
id:"zf123ada123ad",
name:"examp",
subdoc:{
name:"subdoc examp",
subsubdoc:[{
id:"zcgsdf123zaar21",
subsubsubdoc:[{
id:"af2317bh123",
value: "heyhey" //this value I want to update
}]
}]
}
}
let's say i have an reducer that looks like this
The action.payload look something like this
{
theInputId1: "someId",
theInputId2: "anotherId",
theInputValue: "someValue"
}
export function updateSubSubSubDoc(state = {}, action){
switch(action.type){
case 'UPDATE_THE_SUBSUBSUB':
return {
state.doc.subdoc.subsubdoc.find(x => x.id ==
theInputId1).subsubsubdoc.find(x => x.id == theInputId2).value = theInputValue // just example code for you to understand where i'm going.
}
default:
return state
}
}
What I want to do it update one subsubsub doc in a state that is current
With ES6, this is one way that you could do that:
const initialState = { doc: { subdoc: { subsubdoc: {} } } };
export function doc(state = initialState, action) {
switch (action.type) {
case 'UPDATE_THE_SUBSUBSUB':
const subsubdocIdx = state.doc.subdoc.
subsubdoc.find(s => s.id == action.theInputId1);
const subsubdoc = state.doc.subdoc.subsubdoc[subsubdocIdx];
const subsubsubdocIdx = state.doc.subdoc.
subsubdoc[subsubdocIdx].
subsubsubdoc.find(s => s.id == action.theInputId2);
const subsubsubdoc = state.doc.subdoc.
subsubdoc[subsubdocIdx].
subsubsubdoc[subsubsubdocIdx];
return {
...state,
doc: {
...state.doc,
subdoc: {
...state.doc.subdoc,
subsubdoc: [
...state.doc.subdoc.subsubdoc.slice(0, subsubdocIdx),
{
...subsubdoc,
subsubsubdoc: [
...subsubdoc.slice(0, subsubsubdocIdx),
{
...subsubsubdoc,
value: action.theInputValue,
},
...subsubdoc.subsubsubdoc.slice(subsubsubdocIdx + 1, subsubdoc.subsubsubdoc.length - 1),
],
},
...state.doc.subdoc.subsubdoc.slice(subsubdocIdx + 1, state.doc.subdoc.subsubdoc.length - 1),
]
}
}
}
default:
return state;
}
}
(I haven’t tested this code.)
This is nested the same level as in your example, but you might consider using something like combineReducers to make this a little easier to manage. This is also presupposing you have other actions that create the document chain along the way, and you know these documents exist.
Here's an example how you might be able to do it with combineReducers:
function doc(state = {}, action) {
}
function subdoc(state = {}, action) {
}
function subsubdoc(state = [], action) {
}
function subsubsubdoc(state = [], action) {
switch (action.type) {
case 'UPDATE_THE_SUBSUBSUB':
const idx = state.find(s => s.id == action.theInputId2);
return [
...state.slice(0, idx),
{
...state[idx],
value: action.theInputValue,
},
...state.slice(idx + 1),
];
default:
return state;
}
}
export default combineReducers({
doc,
subdoc,
subsubdoc,
subsubsubdoc,
});
In this example, you don't need action.theInputId1, but you would need to store some reference in the data from the subsubdoc to the subsubsubdoc so that when you're rendering, you can piece it back together. Same with all of the other layers.
Related
it is a common pattern to use the switch statement with useReducer hook to handle complex states, but the complexity rule thrown error if the number of cases is too high. what are the alternatives to refactor this code to satisfy this rule?
export type SearchState = {
search: string;
markets: Market[];
};
type SearchAction =
| { type: SearchActionKind.UpdateSearch; search: string }
| { type: SearchActionKind.LoadInitialMarkets; markets: Market[] };
const searchReducer = (prevState: SearchState, action: SearchAction): SearchState => {
switch (action.type) {
case UpdateSearch:
return { ...prevState, search: action.search };
case LoadInitialMarkets:
return { ...prevState, markets: action.markets };
// ... many other cases
default:
return prevState;
}
};
Could you use a map to reduce the switch statement?
const map = {};
map[UpdateSearch] = 'search';
map[LoadInitialMarkets] = 'markets';
if (map.hasOwnProperty(action.type)) {
const key = map[action.type];
const result = { ...prevState };
result[key] = action[key];
return result;
}
switch (action.type) {
// ... the complicated cases
default:
return prevState;
}
Hello i just started redux with react native i am trying to make a food delivery app. each food has options like drinks or dessert for example. I would like that if the user adds a basket an items we check if the options chosen are the same to increase the quantity if they are not the same I would like to add another item with the new option to the global state. only I tried a lot of things and none seemed to meet my expectations. the structure of the data that I pass to the global state is in this form.
cartProduct = [
{
id: itemId,
restaurantName: restaurantName,
name: itemName,
quantity: quantity,
price: price
selectedOption: [
optionId: optionId,
itemId: itemId,
name: itemName,
]
}
]
Here is the code I wrote in the reducers with the add to cart action
switch (action.type) {
case 'ADD_TO_CART':
const cartProductIndex = state.cartProduct.findIndex(item => item.id === action.value.id)
if (cartProductIndex == -1) {
nextState = {
...state,
cartProduct: [...state.cartProduct, action.value]
}
} else {
state.cartProduct[cartProductIndex].selectedOption.map(item => {
if (item.item === action.value.selectedOption.item) {
let newArray = [...state.cartProduct]
newArray[cartProductIndex] = {
...newArray[cartProductIndex],
quantity: state.cartProduct[cartProductIndex].quantity + 1,
totalPrice: state.cartProduct[cartProductIndex].totalPrice + action.value.totalPrice
}
nextState = {
...state,
cartProduct: newArray
}
} else {
nextState = {
...state,
cartProduct: [...state.cartProduct, action.value]
}
}
})
}
return nextState || state
I would do something like this.
switch (action.type) {
case 'ADD_TO_CART':
const cartProductIndex = state.cartProduct.findIndex(item => item.id === action.value.id)
if (cartProductIndex == -1) {
return {
...state,
cartProduct: [...state.cartProduct, action.value]
}
}
const newCartProduct = state.cartProduct[cartProductIndex].selectedOption.map(item => {
if (item.item === action.value.selectedOption.item) {
item.quantity++
}
return item
})
return {
...state,
cartProduct: newCartProduct
}
}
You cannot update the state in a map loop, a map returns a new array. You want to use map to create a new array, and then use that value. Once a function is complete you should return, you don't need that else.
i am creating a file system App using react-redux and am new to this.
i just want to append the latest data into previous data.
here is my code snippet
action.js
export function getFolderList(url){
const local_url_LOW = isLocal ? lowJSON : url + '?fileFidelity=LOW';
const lowRequest = axios.get(local_url_LOW);
return{
type: GET_FOLDER_LIST,
payload: lowRequest
};
}
reducer.js
export default function (state = INITIAL_STATE, action) {
switch (action.type) {
case GET_FOLDER_LIST:
let data = action.payload.data.Files.File
folderData = Array.isArray(data) ? data : [data];
if (folderData.length) {
for (var i = 0; i < folderData.length; i++) {
lst.push({
id: folderData[i].oid,
name: folderData[i].location,
type: folderData[i].type,
childCount: folderData[i].childCount,
createdDate: folderData[i].createdDate,
lastModifiedDate: folderData[i].lastModifiedDate,
location: folderData[i].location
});
}
return lst;
}
break;
}
return state;
}
so i want to return data like .. state.chlidFiles : lst
so can anyone tell me hoe to do this.
currently when my returning lst array it is creating new state with the previous and the lst arrya, but i want to add this new lst array in previous state's childFiles(or you can say to a specific property)
i have used redux-promise for promises.
I am shooting a blind suggestion here. I would use redux-thunk for async operation, then I would dispatch this and use in my reducer.
action
const getFolderList = (url) =>
( dispatch) => {
const local_url_LOW = isLocal ? lowJSON : url + '?fileFidelity=LOW';
const lowRequest = await axios.get(local_url_LOW);
const data = lowRequest.data.Files.File;
const folderData = Array.isArray(data) ? data : [data];
const payload = folderData.map( file => ( {
id: file.oid,
name: file.location,
type: file.type,
childCount: file.childCount,
createdDate: file.createdDate,
lastModifiedDate: file.lastModifiedDate,
location: file.location,
} ) );
dispatch( {
type: GET_FOLDER_LIST,
payload,
} )
}
reducer
export default function ( state = INITIAL_STATE, action ) {
switch ( action.type ) {
case GET_FOLDER_LIST:
return { ...state, childFiles: [ ...state.childFiles, ...action.payload ] };
default:
return state;
}
}
This is one of my redux reducers and I feel it looks very ugly. Is it possible to improve it?
The goal that I want to achieve is very simple:
If I already have this item in my current state, increase the quantity by 1,
otherwise add this item to state.
function globalReducer(state = initialState, action) {
switch (action.type) {
case ADD_TO_CART: {
let { item } = action;
if (state.getIn(['sideCart', 'orderItems', item.id])) {
item.quantity = state.getIn(['sideCart', 'orderItems', item.id]).get('quantity') + 1;
} else {
item.quantity = 1;
}
item = fromJS(item);
const newState = state.setIn(['sideCart', 'orderItems', item.get('id')], item);
return newState;
}
default:
return state;
}
}
The state should look like this:
sideCart: {
orderItems: {
1: {
id: 'orderItems-1',
name: 'AI Brown\'s BBQ Fillet of Beef with Brown Mushroom Brandy Sauce',
quantity: 10,
price: 12,
subitems: ['0', '1', '2'],
instruction: 'No rosemary for beef',
},
2: {
id: 'orderItems-2',
name: 'AI Brown\'s BBQ Fillet',
quantity: 10,
price: 14,
subitems: ['0', '1', '2'],
instruction: 'No rosemary for beef',
},
},
}
This is how I would enhance it syntactically:
const reduceCart = (state, action) => {
let { item } = action;
const stateIn = state.getIn(['sideCart', 'orderItems', item.id]);
item.quantity = stateIn
? stateIn + 1
: 1;
item = fromJS(item);
return state.setIn(['sideCart', 'orderItems', item.get('id')], item);
};
const globalReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TO_CART: return reduceCart(state, action);
default: return state;
}
};
I found the same complexity when using immutable.js to handle deeply nested objects. I have made a lightweight immutable helper: ImmutableAssign that allows you to continue working with plain JS objects, which will simplify your operations.
In the following example, it expects state and action to be plain JS objects, and it will return you a new state as plain JS object:
function globalReducer(state = initialState, action) {
switch (action.type) {
case ADD_TO_CART: return addCart(state, action);
default: return state;
}
}
function addCart(state, action) {
let { item } = action;
return iassign(state,
function(state, context) {
return state.sideCart.orderItems[context.item.id]
},
function(selectedItem) {
item.quantity = selectedItem.quantity ? selectedItem.quantity + 1 : 1;
return item;
},
{ item: item });
}
// The first parameter is a function that return the
// property you need to modify in your state.
// This function must be **pure function**, therefore "item"
// need to be passed in via the context parameter.
//
// The second parameter is a function that modify selected
// part of your state, it doesn't need to be pure, therefore
// you can access "item" directly
//
// The third parameter is the context used in the first
// function (pure function)
I have a reducer working with an object with arrays. I want to change a value on the nested arrays based on a given index. This code works but I can't seem to get my test to work using deep freeze. I was trying to look at the redux example here http://redux.js.org/docs/basics/Reducers.html using .map to find the index with no luck. Any ideas?
export default function myReducer(state = { toDisplay: [] }, action) {
const { type, groupIndex, itemIndex } = action;
const newObject = Object.assign({}, state);
switch (type) {
case actionTypes.TOGGLE_GROUP:
newObject.toDisplay[groupIndex].isSelected = newObject.toDisplay[groupIndex].isSelected ? false : 'selected';
return newObject;
case actionTypes.TOGGLE_ITEM:
newObject.toDisplay[groupIndex].values[itemIndex].isSelected = newObject.toDisplay[groupIndex].values[itemIndex].isSelected ? false : true;
return newObject;
default:
return state;
}
}
EDIT:
For anyone curious after watching a helpful redux video I came up with this:
export default function myReducer(state = { toDisplay: [] }, action) {
const { type, groupIndex, itemIndex } = action;
switch (type) {
case actionTypes.TOGGLE_GROUP:
return {
...state,
toDisplay: [
...state.toDisplay.slice(0, groupIndex),
{
...state.toDisplay[groupIndex],
isSelected: state.toDisplay[groupIndex].isSelected ? false : 'selected'
},
...state.toDisplay.slice(groupIndex + 1)
]
};
case actionTypes.TOGGLE_ITEM:
return {
...state,
toDisplay: [
...state.toDisplay.slice(0, groupIndex),
{
...state.toDisplay[groupIndex],
values: [
...state.toDisplay[groupIndex].values.slice(0, itemIndex),
{
...state.toDisplay[groupIndex].values[itemIndex],
isSelected: state.toDisplay[groupIndex].values[itemIndex].isSelected ? false : true
},
...state.toDisplay[groupIndex].values.slice(itemIndex + 1)
]
},
...state.toDisplay.slice(groupIndex + 1)
]
};
default:
return state;
}
}
Using a helper/library like suggested is probably the best route but my team wishes to not add another dependency.
Firstly, Object.assign(...) only does a shallow copy. See here.
As you have objects nested inside arrays nested inside objects I would highly recommend the immutability helpers from react (as mentioned by Rafael). These allow you to do something like this:
case actionTypes.TOGGLE_GROUP:
return update(state, {
toDisplay: {
[groupIndex]: {
isSelected: {$set: newObject.toDisplay[groupIndex].isSelected ? false : 'selected'}
}
}
});
If you are looking to modify a simple value inside an array with raw js then you can use something like this:
return list
.slice(0,index)
.concat([list[index] + 1])
.concat(list.slice(index + 1));
(source)