React-Redux update part of the state - reactjs

I have the following initial state for my react-redux
const initialState = {
logged: false,
user: null,
company: null,
}
When I first login, I set the state data
const data = action.payload.data;
const properties = action.payload.properties;
const notifications = action.payload.notifications;
return {
...state,
logged: true,
user: {
id: data.UserID,
firstName: data.UserFName,
lastName: data.UserLName,
email: data.UserEmail,
securityLevel: parseInt(data.SecurityLevelID),
notifications: {
multiProp: notifications.MultiProp && parseInt(notifications.MultiProp) === 1 ? false : true
}
},
company: {
id: data.CompanyID,
name: data.CompanyName,
email: data.ContactEmail,
leadSource: parseInt(data.LeadSourceCompanyID)
}
}
Then, at some point of my project, I need to update only user.notifications.multiProp, so I created a new Type called UPDMULTIPROP and I'm doing:
case Types.UPDMULTIPROP:
const userData = action.payload.data;
return {
...state,
user: {
notifications: {
multiProp: userData.notifications.MultiProp && parseInt(userData.notifications.MultiProp) === 1 ? false : true,
}
}
}
However, it set all the other user state to undefined. How can I update just the multiProp?
Thanks

user is also an object, to keep user state the same you should also spread the user.
case Types.UPDMULTIPROP:
const userData = action.payload.data;
return {
...state,
user: {
...state.user,
notifications: {
multiProp: userData.notifications.MultiProp && parseInt(userData.notifications.MultiProp) === 1 ? false : true,
}
}
}

To update nested object immutably you need to spread it again. Like
user : { ...state.user, notifications : someValue }
Like this.

Related

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

How to destruct object in reducer (REACT)

I have the following userData in state
userData : {
isValidCheckup: true,
accounts: {
userAccount: [
{
accountType: 'checkings',
includeInCheckup: false
},
{
accountType: 'checkings',
includeInCheckup: false
}
]
}
Now, I have a reducer to handle manipulation of above data. Note: I only want to update includeInCheckup field. Depending on the accountType recieved as a payload to the reducer.
Here is the reducer function I am using
case UPDATE_USER_DATA: {
const { field, value } = action.payload; // field='checkings', value=true
return {
...state,
userData: {
...state.userData
}
}
}
As you can see, I am getting field='checkings', value=true from the payload to the reducer. How can I specifically update this value to the includeInCheckup property based on the field value.
Can someone please shed some light.
When I get field as 'checkings', I update the first element of array
'userAccount' and when I get field as 'savings, I will update the
second element of array.
For example state, I am assuming userAccount[0] is a checking account type, and userAccount[1] is a savings account type, and the array will always be length 2.
userData : {
isValidCheckup: true,
accounts: {
userAccount: [
{
accountType: 'checkings',
includeInCheckup: false
},
{
accountType: 'savings',
includeInCheckup: false
}
]
}
Copy each level of state that is being updated, and then update the property by creating a new object/array reference. You can simply map the userAccount array and check if the account type matches that of the action payload field, and update the includeInCheckup property.
case UPDATE_USER_DATA: {
const { field, value } = action.payload; // field='checkings', value=true
return {
...state,
userData: {
...state.userData,
accounts: {
...state.userData.accounts,
userAccount: state.userData.accounts.map((account) =>
account.accountType === field
? {
...account,
includeInCheckup: value
}
: account
)
}
}
};
}

What happens when a reducer returns 'state' in React?

If I have a contactReducer that looks like this:
import {
GET_CONTACTS,
DELETE_CONTACT,
ADD_CONTACT,
EDIT_CONTACT,
GET_CONTACT,
} from "../actions/types";
// the state the holds the contacts
const initialState = {
contacts: [
{
id: 1,
name: "John Doe",
email: "john#gmail.com",
phone: "555-555-5555",
},
{
id: 2,
name: "Karen Williams",
email: "karen#gmail.com",
phone: "444-444-4444",
},
{
id: 3,
name: "Henry Johnson",
email: "henry#gmail.com",
phone: "333-333-333",
},
],
contact: {},
testProp: {},
};
export default function (state = initialState, action) {
switch (action.type) {
case GET_CONTACTS:
console.log("get contacts");
return {
...state,
};
case DELETE_CONTACT:
return {
...state,
contacts: state.contacts.filter(
(contact) => contact.id !== action.payload
),
};
case ADD_CONTACT:
let newArray = state.contacts.slice(); // get the current contacts array
newArray.unshift(action.payload); //push on the new contact to the beg of array
return {
...state, //take the existing state..
contacts: newArray,
};
case EDIT_CONTACT:
console.log("trying to edit");
return {
...state,
contacts: state.contacts.map((contact) =>
contact.id == action.id ? (contact = action.payload) : contact
),
};
case GET_CONTACT:
const selectedContact = getSingleContactFromId(state, action.payload);
console.log("look here");
console.log(selectedContact);
return {
...state,
contact: selectedContact,
testProp: { test: "test prop" },
};
default:
console.log("testing action in default");
return state;
}
}
function getSingleContactFromId(state, id) {
var contact;
console.log("get single contact");
for (var i = 0; i < state.contacts.length; i++) {
contact = state.contacts[i];
if (contact.id == id) {
return contact;
}
}
}
What is actually happening when the reducer returns? Where does it return to? For example, I send a dispatch to the reducer like this this.props.addContact(newContact);
But, I don't see that I do anything with the returned object anywhere after this. In another file, is where I grab things from the state, so does return really just mean it is updating the state?
Assuming you use combineReducers, the returned state from a specific reducer will now be the updated state of the state chunk represented by this reducer.
Then, any connected component will receive the new state and will re-render.
This is a high-level description obviously.
More information can be found here: https://react-redux.js.org/using-react-redux/connect-mapstate

action structure to get state from redux using reducers

I'm using redux actions to set some contacts list. I want to get those contacts using a redux action. but all i get is the contact set in the action. Can you tell me how to get the actual state contact.
action.ts
export const setCurrentContact = (contact: IContactObject) => ({
type: 'SET_CURRENT_CONTACT',
contact,
})
export const getCurrentContact = () => ({
type: 'GET_CURRENT_CONTACT',
contact: { ID: "", Name: "", Email: "", Mobile: "", Landline: "", Website: "", Address: "" },//when using dispatch i get this contact i.e blank details
})
reducer.ts
const initialContact=[{ ID: "507", Name: "xander", Email: "xander.cage#gmail.com", Mobile: "9999999999", Landline: "4026241799", Website: "www.xandercage.com", Address: "california" }]
export const currentContact = (state = initialContact, action) => {
switch (action.type) {
case 'SET_CURRENT_CONTACT':
{
return [
{
ID: action.contact.ID,
Name: action.contact.Name,
Email: action.contact.Email,
Mobile: action.contact.Mobile,
Landline: action.contact.Landline,
Website: action.contact.Website,
Address: action.contact.Address,
}
]
}
case 'GET_CURRENT_CONTACT':
{
console.log(state[0]);
return state
}
default:
return state
}
}
somefile.ts
interface IDispatchFromProps
{
setCurrentContact: any,
getCurrentContact: any,
}
function mapDispatchToProps(dispatch): IDispatchFromProps
{
return{
setCurrentContact: (contact:IContactObject)=>{dispatch(setCurrentContact(contact))},
getCurrentContact: ()=>{dispatch(getCurrentContact())},//this should give me the initial data
}
}
expected output:
getCurrentContact() gives intial data set in reducer.ts
actual output:
data set in contact key of action.ts
The problem is that when you dispatch 'SET_CURRENT_CONTACT' action type, you crush the existing state.
instead of :
return [
{
ID: action.contact.ID,
...rest of your payload
}
]
do this :
return [
...state,
{
ID: action.contact.ID,
...rest of your payload
}
]
Hope it helps !

How to return state of Redux reducers

For redux reducer step :
What if I only want to change one single property of initial_state. For example:
const INITIAL_STATE = {
signInInfo: {
signin: false,
name: "",
email: "",
...
},
changePassword: {
status: false,
...
}
...
};
Here I only want to set signInInfo.signin as true, currently , the only way I know is to input a complete "signInInfo" like :
case SIGNIN_USER:
return { ...state, signInInfo: action.payload.data };
action.payload.data is like:
{
signin: true,
name: "Qing",
email : ...
}
And another question is what if I want to set signInInfo.signin as false and meanwhile also need to change changePassword.status from false to true.
What should I do? Can anyone give me a hint?
Fairly simple (both questions):
case SIGNIN_USER: return {
...state,
signInInfo: {
...state.signInInfo,
signin: action.payload.signin
},
changePassword: {
...state.changePassword,
status: true
}
};

Resources