//i need to add objects into a array using reducer for contact book
//reducer
const addContact = (contacts = [], action) => {
let contactsArr = [{}];
if (action.type = "ADD_CONTACT") {
return [...contactsArr, action.payload];
}
return contacts;
};
actions
export const addContactRed = contact => {
return {
type: "ADD_CONTACT",
payload: contact
};
};
{
type:"ADD_CONTACT",
payload:{name:"xyz",phonenum:10101001}
}
{
type:"ADD_CONTACT",
payload:{name:"abc",phonenum:0101001}
}
//after dispatching two actions final array i want is
//contactsArr
[
{name:"xyz",phonenum:10101001},
{name:"abc",phonenum:0101001}
]
You don't have ton init let contactsArr = [{}]; It will reset the store value in your reducer. Just use the contact store variable
const addContact = (contacts = [], action) => {
// if (action.type = "ADD_CONTACT") {
if (action.type === "ADD_CONTACT") {
return [...contacts, action.payload];
}
return contacts;
};
Related
I have an array, every time I fire an action it adds a new item in the array with the value true and or false, I need to change that code to return the real number of the array not adding new items
Here is my code,
import {ads} from '../../data/ads';
import {ADD_TO_FAVORITE} from '../types';
interface ActionInter {
type: string;
payload: {id: number};
}
const initialState = {
allAds: ads,
myFavorite: [],
};
const myFavorite = (state = initialState, action: ActionInter) => {
switch (action.type) {
case ADD_TO_FAVORITE:
const itemFav = state.allAds[action.payload.id - 1].isFav;
console.log(itemFav);
if (itemFav === true)
return {
...state,
allAds: [
...state.allAds,
(state.allAds[action.payload.id - 1].isFav = false),
],
};
if (itemFav === false)
return {
...state,
allAds: [
...state.allAds,
(state.allAds[action.payload.id - 1].isFav = true),
],
};
}
return state;
};
export default myFavorite;
ads, it's an array of objects
You can using map like this:
const myFavorite = (state = initialState, action: ActionInter) => {
switch (action.type) {
case ADD_TO_FAVORITE:
return {
...state,
allAds: state.allAds.map((item, index) => {
return action.payload.id - 1 === index
? {
...item,
isFav: !item.isFav,
}
: item;
}),
};
}
return state;
};
You can try this:
const myFavorite = (state = initialState, action: ActionInter) => {
switch (action.type) {
case ADD_TO_FAVORITE:
const index = action.payload.id - 1;
const newAllAds = [...state.allAds];
newAllAds[index].isFav = !newAllAds[index].isFav;
return {
...state,
allAds: newAllAds,
};
}
return state;
};
You have to use immutability, instead of updating the previous state, create a copy and update that copy
const myFavorite = (state = initialState, action: ActionInter) => {
switch (action.type) {
case ADD_TO_FAVORITE: {
// Using the spread operator we'll create a copy
const allAdsCopy = [...state.allAds];
// Identify the item index
const index = action.payload.id -1;
// Update the copy
allAdsCopy[index].isFav = !allAdsCopy[index].isFav;
return {
...state,
allAds : allAdsCopy,
};
}
default:
return state;
}
};
I just started using NextJS for the first time and I'm trying to use it with Redux.
I have been able to set up the redux and set up the getServerSideProps function in index.js.
The problem however is that the store returns an empty state even after I have fetched the data. When I console.log the fetched data from inside the getServerSideProps function, I see it in my terminal, so I'm left to suspect that the problem is that my action creator is not dispatching the data to the store. I even tried to handle the data fetching and dispatch from inside the action creator using redux-thunk, but that also did not work. Please I really need some help with this.
My action creator:
export const initializeArticles = (articles) => {
return {
type: 'INITARTICLES',
data: articles
}
}
My reducer:
const articlesReducer = (state = initialState, action) => {
switch (action.type) {
case 'INITARTICLES':
return action.data
case 'ADDARTICLE':
return state.concat(action.data)
case 'INCREMENTARTICLELIKES':
return state.map((article) => {
if(article.id === action.id) {
return {
...article,
likes: article.likes + 1
}
} else {
return article
}
})
case 'DELETEARTICLE':
return state.filter(article => article.id !== action.id)
case 'ADDCOMMENT':
return state.map((article) => {
if (article.id === action.id) {
return {
...article,
comments: article.comments.concat(action.data)
}
} else {
return article
}
})
case 'APPROVECOMMENT':
return state.map((article) => {
if (article.id === action.articleId) {
return article.commentIsApproved = true
} else {
return article.commentIsApproved = false
}
})
default:
return state
}
}
export default articlesReducer
My store setup:
let store;
const initstore = (initialState) => {
return createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(thunk))
)
}
export const initializeStore = (preloadedState) => {
let _store = store ?? initstore(preloadedState)
if (preloadedState && store) {
_store = initstore({
...store.getState(),
...preloadedState
})
store = undefined
}
if(typeof window === 'undefined') return _store
if(!store) store = _store
return _store
}
export function useStore(initialState) {
const store = useMemo(() => initializeStore(initialState), [initialState])
return store
}
My getServerSideProps function:
export const getServerSideProps = () => {
const reduxStore = initializeStore()
const { dispatch } = reduxStore
getPosts().then((posts) => {
console.log(posts)
dispatch(initializeArticles(posts))
})
return { props: { initialReduxState: reduxStore.getState() } }
}
export default Home
This is the code that I used in the reducer to add items to cart, the return statement below is what I was trying to use to not allow any duplicate items in the cart but to change only the quantity of the item. But it still did not work. The quantity does update but I still end up with duplicate values. So please help me figure this out, Thx
export const initialState = {
cart: []
}
const Reducer = (state = initialState, action) =>{
switch(action.type){
case "ADD_TO_CART":
const {id, quantity} = action.item
let alreadyInCart = false, newQty=0
state.cart.forEach(x =>{
if(x.id === id){
alreadyInCart = true
newQty = parseInt(x.quantity+quantity)
}
})
//let categories = [...new Set(Inventory.map(x=>x.category))]
if(!alreadyInCart){
return {
...state,
cart: [...state.cart, action.item]
}
} else{
***return {
...state,
cart: [...state.cart,
{...action.item, quantity: newQty}
]
}***
}
default:
return state
}
}
export default Reducer
Looks like you just need to update the existing item in the cart, or add it to the cart.
Try this.
export const initialState = {
cart: [],
};
const Reducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_TO_CART':
const nextCart = [...state.cart];
const existingIndex = nextCart.findIndex((item) => item.id === action.item.id);
if (existingIndex >= 0) {
const newQuantity = parseInt(nextCart[existingIndex].quantity + action.item.quantity);
nextCart[existingIndex] = {
...action.item,
quantity: newQuantity,
};
} else {
nextCart.push(action.item);
}
return {
...state,
cart: nextCart,
};
default:
return state;
}
};
export default Reducer;
So I'm creating something like "Trello" clone with react redux nodejs and mongoDB and i have some issue.
The problem is when I add a card to a list its not update the redux state, so I will see the card in the list only after a refresh page. (the card added to the DB but not to redux state).
just for more info: boardlists is an array inside the object board from mongo, inside that array there is objects of list, inside each of them there is an array of cards.
here is my code:
REDUCER
const initialState = {
boardLists: [
],
};
export default function (state = initialState, action) {
switch (action.type) {
case FETCH_ITEMS_BEGIN:
return {
...state,
loading: true,
errors: null,
};
case FETCH_ITEMS_SUCCESS:
return {
...state,
loading: false,
boardLists: action.payload.data.boardLists,
};
case FETCH_ITEMS_FAILURE:
return {
...state,
loading: false,
errors: action.payload.errors,
boardLists: [],
};
//handless creation of data
case ADD_LIST:
return {
boardLists: [...state.boardLists, action.payload.list],
};
case ADD_CARD:
return {
boardlists: [...state.boardlists, action.payload.card],
}
ACTIONS
export const fetchItemsBegin = () => ({
type: FETCH_ITEMS_BEGIN,
});
export const fetchItemsSuccess = (data) => ({
type: FETCH_ITEMS_SUCCESS,
payload: { data },
});
export const fetchItemsFailure = (errors) => ({
type: FETCH_ITEMS_FAILURE,
payload: { errors },
});
//dispatched when item needs to be created
export const addList = (list) => {
return {
type: ADD_LIST,
payload: { list },
};
};
// add card
export const addCard = (card) => {
return {
type: ADD_CARD,
payload: { card }
};
};
//dispatched when all the lists from board stored in redux store needs to be read
export const getBoardLists = () => {
return (dispatch) => {
// function starts
dispatch(fetchItemsBegin()); // fetching begins
return http
.get(`${myUrl}/boards/one`) // req data from server
.then(({ data }) => {
console.log(data);
// if data is found
dispatch(fetchItemsSuccess(data)); // success
})
.catch((error) => dispatch(fetchItemsFailure(error))); //errors
};
};
COMPONENT THAT HANDLE THE ADD FUNCTION
handleAddCard = () => {
//add card
const { text } = this.state;
const { listID } = this.props;
const newCard = {
// _id: uuidv4(),
text,
};
cardService.createCard(newCard, listID);
this.props.addCard(newCard);
};
.
.
.
.
.
const mapStateToProps = ({ boardLists, loading, errors }) => ({
boardLists,
loading,
errors,
});
export default connect(mapStateToProps, { addList, addCard, getBoardLists })(ActionButton);
It appears you need to update an object in your lists array, and not add the card item to the list array itself.
In the actions:
// add card
export const addCard = (card, listId) => {
return {
type: ADD_CARD,
payload: { listId, card }
};
};
In the Reducer, you will need to find the list with matching id and add the card to its array e.g.:
case ADD_CARD:
const {listId, card} = action.payload;
return {
...state,
boardLists: state.boardLists.map(list => {
list.cards = list.cards || [];
return list.id === listId ? {...list, cards: [...list.cards, card]} : list
}),
}
This other question on stack overflow could be useful for this part. link
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.