react redux not updating view component - javascript

my view is now updating when redux state is changed, but if i click button "toggleToDoReducer" the view is updating to the new state.
here's my code
const toggleToDoReducer = (state, payload) => {
return ({
...state,
items: state.items.map((item, index) => {
if (index === payload) {
return { text: item.text, isChecked: !item.isChecked }
}
return item
})
})
}
shuffleMe = (oldData) => {
let i, j, temp;
for (i = oldData.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
temp = oldData[i];
oldData[i] = oldData[j];
oldData[j] = temp;
}
let newArray = oldData;
return (newArray)
}
const shuffleReducer = (state) => {
return ({
...state,
items: shuffleMe(state.items)
})
}
any help would be great appreciated

I think shuffleMe function is mutating the state directly. Rather pass the copy of the state. An sample implementation below
const toggleToDoReducer = (state, payload) => {
return {
...state,
items: state.items.map((item, index) => {
if (index === payload) {
return { text: item.text, isChecked: !item.isChecked };
}
return item;
})
};
};
const shuffleMe = oldData => {
oldData.forEach((element, i) => {
const j = Math.floor(Math.random() * (i + 1));
[oldData[i], oldData[j]] = [oldData[j], oldData[i]];
});
return oldData;
};
const shuffleReducer = state => {
return {
...state,
items: shuffleMe([...state.items])
};
};

Try with setState({}) function for re-render your view.

Related

Immer An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft

hi .
I am planning to create a shopping cart system for my site. I use React and Redux toolkit. But when I want to dispatch my states, I encounter this error. Thank you for helping me.
cartSlide (Cart Reducer) :
import {createSlice} from "#reduxjs/toolkit";
const initialState = {
selectedItems: [],
itemsCounter: 0,
total: 0,
checkout: false
}
const sumItems = items => {
const itemsCounter = items.reduce((total , product) => total + product.quantity, 0)
const totalPrice = items.reduce((total , product) => total + product.price * product.quantity,0).toFixed(2)
return {
itemsCounter,
totalPrice
}
}
export const cartSlice = createSlice({
name: 'cart',
initialState,
reducers: {
AddItem:(state,action)=>{
if (!state.selectedItems.find(item => item.id === action.payload.id)) {
state.selectedItems.push({
...action.payload,
quantity: 1
})
}
return {
...state,
selectedItems: [...state.selectedItems],
...sumItems(state.selectedItems),
checkout: false
}
},
RemoveItem: (state, action) => {
const newSelectedItems = state.selectedItems.filter(item => item.id !== action.payload.id);
return {
...state,
selectedItems: [...newSelectedItems],
...sumItems(newSelectedItems)
}
},
Increase: (state, action) => {
const indexI = state.selectedItems.findIndex(item => item.id === action.payload.id);
state.selectedItems[indexI].quantity++;
return {
...state,
...sumItems(state.selectedItems)
}
},
Decrease: (state, action) => {
const indexD = state.selectedItems.findIndex(item => item.id === action.payload.id);
state.selectedItems[indexD].quantity--;
return {
...state,
...sumItems(state.selectedItems)
}
},
Checkout: () => {
return {
selectedItems: [],
itemsCounter: 0,
total: 0,
checkout: true
}
},
Clear: () => {
return {
selectedItems: [],
itemsCounter: 0,
total: 0,
checkout: false
}
}
}
})
export const {AddItem,RemoveItem,Increase,Decrease,Checkout,Clear} = cartSlice.actions
export default cartSlice.reducer
The error is for the AddItem action, and when I delete the return part, the code works.
this part:
AddItem:(state,action)=>{
if (!state.selectedItems.find(item => item.id === action.payload.id)) {
state.selectedItems.push({
...action.payload,
quantity: 1
})
}
return {
...state,
selectedItems: [...state.selectedItems],
...sumItems(state.selectedItems),
checkout: false
}
},
Don't both modify the state object state.selectedItems.push and use a return.
Option A - Modify Draft
AddItem: (state,action) => {
// Check if item exists
if (!state.selectedItems.find(item => item.id === action.payload.id)) {
// add item since it did not exist.
state.selectedItems.push({
...action.payload,
quantity: 1
})
}
// Calculate new count and totals.
const sum = sumItems(state.selectedItems);
// Apply new count and totals to the state.
state.itemsCounter = sum.itemsCounter;
state.totalPrice = sum.totalPrice;
// Set checkout to false.
state.checkout = false;
}
Option B - New State
AddItem: (state,action) => {
// create a new array containing selected items.
let newItems = [
...state.selectedItems;
];
// check if item already exists
if (!newItems.find(item => item.id === action.payload.id)) {
// add item since it did not exist
newState.selectedItems.push({
...action.payload,
quantity: 1
});
}
// return a new state
return {
...state,
selectedItems: newItems,
...sumItems(newItems),
checkout: false
}
}
You should probably stick with modifying the draft method unless you intend to replace the state with a completely new state. Using the draft method Immer should handle the reference updates where needed and allows you to write code as if you were editting a normal mutable object (for the most part, see the docs for some limitations).

React Native - Class Component to Functional Component (UseEffect: state undefined)

I need to convert my Class Components code to Functional Hooks Components. I have the Class Components logic below that works. However for me to implement Context Api I need to convert it to hooks.
I get storeList undefined when I console log it and this error...
TypeError: undefined is not an object (evaluating 'storeList'.map).
Is there a way I can make introduce a state inside a UseEffect? Thank you in advance
const { data, status } = useQuery('stores', fetchStores)
const [isSelected, setIsSelected] = useState(false)
const storeList = data
useEffect(() => {
let array = storeList.map((item, index) => {
isSelected = false
return { ...item }
})
setIsSelected({ ...isSelected, array: { ...storeList.array } })
selectHandler()
}, [])
const selectHandler = (ind) => {
let array = storeList.map((item, index) => {
if (ind == index) {
item.isSelected = !item.isSelected
}
return { ...item }
})
setIsSelected({ ...isSelected, array: { ...storeList.array } })
}
Here is the same code as Class component and it works perfectly
async componentDidMount() {
let array = this.state.storeList.map((item, index) => {
this.isSelected = false
return { ...item }
})
this.setState({ storeList: array })
}
selectionHandler = (ind) => {
const { storeList } = this.state
let array = storeList.map((item, index) => {
if (ind == index) {
item.isSelected = !item.isSelected
}
return { ...item }
})
this.setState({ storeList: array })
}
Try This:
const { data, status } = useQuery('stores', fetchStores)
const [isSelected, setIsSelected] = useState(false)
const storeList = data
useEffect(() => {
let array = storeList.map((item, index) => {
isSelected = false
return { ...item }
})
setIsSelected(state => ({ ...state, array: { ...storeList.array } }));
selectHandler()
}, [])
const selectHandler = (ind) => {
let array = storeList.map((item, index) => {
if (ind == index) {
item.isSelected = !item.isSelected
}
return { ...item }
})
setIsSelected(state => ({ ...state, array: { ...storeList.array } }));
}
Reference: https://stackoverflow.com/a/63522873/10994570
This worked!! I hope it can help someone else. Thanks
const { data, status } = useQuery('stores', fetchStores)
const [isSelected, setIsSelected] = useState(false)
const storeList = data
useEffect(() => {
handlerControl()
}, [])
const handlerControl = async () => {
try {
let array = await storeList.map((item, index) => {
isSelected = false
return { ...item }
})
setIsSelected(array)
} catch (e) {
console.log(e)
}
}
const selectHandler = async (ind) => {
//alert('pressed')
try {
let array = await storeList.map((item, index) => {
if (ind == index) {
item.isSelected = !item.isSelected
}
return { ...item }
})
setIsSelected(array)
console.log(index)
} catch (e) {
console.log(e)
}
}

useReducer dispatch executing twice

I don't understand why this happens. Since strict mode is enabled in react, this function gets executed twice. So instead of deleting a single item, it deletes two items, one on first round, second on the next.
const deleteItem = (state, index) => {
// index is a number
let indexCounter = 0;
let tempArray = [...state.todos];
const newTodos = tempArray.filter(item => {
if (item.index === index) {
return false;
}
item.index = indexCounter++;
return true;
});
return {
...state,
todos: newTodos,
nextIndex: indexCounter
};
}
But if I use a Set instead of primitive data type (number), this works fine. i.e. only one item will be removed, even though dispatch is called twice.
const deleteItem = (state, set) => {
const newSet = new Set(set);
let indexCounter = 0;
let tempArray = [...state.todos];
const newTodos = tempArray.filter(item => {
if (newSet.has(item.index)) {
newSet.delete(item.index);
return false;
}
item.index = indexCounter++;
return true;
});
return {
...state,
todos: newTodos,
nextIndex: indexCounter
};
}
Am I missing something here? What exactly is happening?
You are mutating the state which influences the next action.
// Is a shallow copy
let tempArray = [...state.todos];
const newTodos = tempArray.filter((item) => {
if (item.index === index) {
return false;
}
// State mutation
item.index = indexCounter++;
return true;
});
Instead, you need to make a deep copy or use Immutable Update Pattern as mentioned in Redux docs.
I've updated the code. It's working fine now. Hope it's correct.
const deleteItem = (state, index) => {
let indexCounter = 0;
const tempArray = state.todos.filter(item => {
return index !== item.index;
});
const newTodos = [];
tempArray.forEach((item) => {
newTodos.push({...item, index: indexCounter++});
})
return {
...state,
todos: newTodos,
nextIndex: indexCounter
};
}

add key and value in array in redux

I am trying to update an array in reducer
let initialState = {
count: 0,
todos: [],
id:0,
}
const authReducer = (prevState = initialState, action) => {
switch (action.type) {
case types.ADD_TO_DO:
console.log(action.todo)
return {
...prevState,
todos: prevState.todos.concat(action.todo)
}
default:
return prevState;
}
}
And I am getting array in the form
todos:['qwerty', 'abcdef']
But I want in the form of
todos:[{id:'1', todo:'qwerty'},{id:'2',todo:'abcdef'}]
How can I achieve this?
Thanks!!!
In order to convert todos:['qwerty', 'abcdef'] to your expected format, you can map it:
var todos=['qwerty', 'abcdef'];
var result = todos.map((todo, i)=>({id:i+1, todo}));
console.log(result);
You can use reduce for this task
const todos = ['qwerty', 'abcdef']
const data = todos.reduce((acc, rec, index) => {
return [...acc, {
id: index + 1,
todo: rec
}]
}, [])
console.log(data)

Search functionality in reduxReact-native

I am trying to achieve search functionality in redux
I am using the following code but its not working but the same code is working in plain javascript but not with redux
searchItems: (name ) => async (dispatch) => {
let newData = []
dispatch({
type: types.SEARCH_ITEMS,
payload: (newData = DATA.filter((item) => {
const itemData = `${item.name.toUpperCase()}`;
const textData = name.toUpperCase();
itemData.indexOf(textData) > -1;
return newData;
})),
});
},
};
the newData is returning and empty array
export const reducer = (state = initialState, action) => {
const { type } = action;
switch (type) {
case types.SEARCH_ITEMS:
return [...action.payload];
default:
return state;
}
};
What am i dong wrong?
You should return from the filter callback true or false:
Try to do it this way:
searchItems: (name ) => async (dispatch) => {
dispatch({
type: types.SEARCH_ITEMS,
payload: DATA.filter((item) => {
const itemData = `${item.name.toUpperCase()}`;
const textData = name.toUpperCase();
return itemData.indexOf(textData) > -1;
}),
});
},
};

Categories

Resources