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 !
Related
I want to save my data in localstorage to evade the loss of it when reloading the page but i also need it in my gloable state to show a preview of it once it's added and never be lost when reloading the page,This is my slice format:
import { createSlice } from "#reduxjs/toolkit";
export const resumeSlicer = createSlice({
name: "resume",
initialState: {
Education: [
{
key: NaN,
Title: "",
Date: "",
Establishment: "",
Place: "",
},
],
},
reducers: {
SaveEducation: (state, action) => {
let Education = JSON.parse(localStorage.getItem("Education"));
if (!Education) {
Education.push(action.payload);
localStorage.setItem("Education", JSON.stringify(Education));
state.Education = Education;
} else {
Education.push(action.payload);
let i = 0;
Education.map((e) => {
e.key = i;
i++;
return e.key;
});
localStorage.setItem("Education", JSON.stringify(Education));
state.Education = Education;
}
},
getEducation: (state, action) => {
const items = JSON.parse(localStorage.getItem("Education"));
const empty_array = [
{
key: NaN,
Title: "",
Date: "",
Establishment: "",
Place: "",
},
];
state.Education.splice(0, state.Education.length);
state.Education = items;
},
},
});
And this is how i fetched:
const EdList = useSelector((state) => state.Education);
When i console.log it the result is "undefined"
Image Preview
https://i.stack.imgur.com/hD8bx.png
I'm hazarding a guess that the issue is a missing reference into the state. The chunk of state will typically nest under the name you give the slice, "resume" in this case. This occurs when you combine the slice reducers when creating the state object for the store.
Try:
const EdList = useSelector((state) => state.resume.Education);
If it turns out this isn't the case then we'll need to see how you create/configure the store and how you combine your reducers.
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.
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
Consider these two patterns for reducer's state:
A single state object:
// personReducer.js
const initialState = {
personInfo: {
firstName: "",
lastName: "",
foo: "",
bar: "",
baz: "",
foo1: "",
bar1: "",
bar2: "",
foo2: "",
foo3: "",
}
// some not-to-mention states
};
Multiple states (without the person level):
// personReducer.js
const initialState = {
firstName: "", // no person level
lastName: "",
foo: "",
bar: "",
baz: "",
foo1: "",
bar1: "",
bar2: "",
foo2: "",
foo3: ""
};
Which one is better? What confuses me is:
// Person.jsx - pattern 1
const mapStateToProps = ({ personInfo }) => {
personInfo
}
// Person.jsx - pattern 2
const mapStateToProps = ({ firstName, lastName }) => {
firstName,
lastName
}
// Human.jsx - pattern 1
const mapStateToProps = ({ personInfo }) => {
personInfo
}
// Human.jsx - pattern 2
const mapStateToProps = ({ foo, bar }) => {
foo,
bar
}
Let's say we have 2 components Person and Human in our app, both of them will connect to personReducer to retrieve personal information.
For pattern 1:
In Person component, I dispatch an action to update firstName inside personInfo, which later will force Human to re-render as well, does it? Something like this in our reducer:
case UPDATE_PERSON_INFO: {
return {
...state,
personInfo: {
...state.personInfo,
firstName: payload.firstName
}
}
}
For pattern 2:
In Person component, I dispatch an action to update firstName, which later will not force Human to re-render, because Human is not mapping firstName to its props, but foo, bar. Am I right? Something
like:
case UPDATE_PERSON_INFO: {
return {
...state,
firstName: payload.firstName
}
}
It will re-render in both cases because in both patterns you update the reference to the new immutable state object.
If you want to prevent unnecessary renderings of components you have to use memoized selectors in mapStateToProps. Here is the documentation link and GitHub
These, selectors should be specific for your components.
I have an app which get all categories and products from the server with Redux ACTIONS. I need to filter products with a category Id. after load data action is complete, i call another action to filter products but i'm a little bit confused.
There is codes of few parts of the app:
ProductsActions:
export const GET_INITIAL_PRODUCTS_DATA = "GET_INITIAL_PRODUCTS_DATA";
export const GET_INITIAL_PRODUCTS_DATA_RESULT = "GET_INITIAL_PRODUCTS_DATA_RESULT";
export const GET_INITIAL_PRODUCTS_DATA_ERROR = "GET_INITIAL_PRODUCTS_DATA_ERROR";
export const FILTER_PRODUCTS_BY_CATEGORY_ID = "FILTER_PRODUCTS_BY_CATEGORY_ID";
export const getInitialProductsData = () => ({
type: GET_INITIAL_PRODUCTS_DATA
});
export const filterProductsByCategoryId = categoryId => ({
type: FILTER_PRODUCTS_BY_CATEGORY_ID,
categoryId
});
ProductsReducers:
import {
GET_INITIAL_PRODUCTS_DATA,
GET_INITIAL_PRODUCTS_DATA_RESULT,
GET_INITIAL_PRODUCTS_DATA_ERROR,
FILTER_PRODUCTS_BY_CATEGORY_ID
} from "../actions/products";
const initialState = {
isFetching: false,
data: {},
error: null
};
const filterProductsByCategoryId = (state, action) => {
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case GET_INITIAL_PRODUCTS_DATA:
return {
...state,
isFetching: true
};
case GET_INITIAL_PRODUCTS_DATA_RESULT:
return {
...state,
isFetching: false,
data: action.result
};
case GET_INITIAL_PRODUCTS_DATA_ERROR:
return {
...state,
isFetching: false,
error: action.error
};
case FILTER_PRODUCTS_BY_CATEGORY_ID:
return {
...state,
data: filterProductsByCategoryId(state, action.categoryId)
};
default:
return state;
}
};
export default reducer;
And there is my code to call filter action:
filterProducts = (title = "A") => {
const _categories = Object.values(this.props.categories);
const selectedCategory = _categories.find(
category => category.title === title
);
this.props.dispatch(filterProductsByCategoryId(selectedCategory.id));
My questions is:
A) Is there is a way to filter my data and display them in UI and refresh them without using ACTIONS way??
B) If A's answer is No!, How can i get my state.data and filter them in FILTER_PRODUCTS_BY_CATEGORY_ID?
Thanks.
You can use the Array.prototype.filter() to return filtered result.
keep in mind that this will return an array and not a single value, which is a good thing if you are using this filter within your reducer. because your reducer's shape is an array and not an object.
Running example:
const myData = [{
name: 'some name',
id: 1
}, {
name: 'some name2',
id: 2
}, {
name: 'some name3',
id: 3
}, {
name: 'some name4',
id: 4
}]
const filterProductsByCategoryId = (state, action) => {
return state.filter(c => c.id === action.categoryId);
};
const result = filterProductsByCategoryId(myData, {categoryId: 2});
console.log(result);
I think it is more appropriate to create a selector for a singular product that will handle this kind of action, this way you will be able to return an object instead of an array with one product in it.
Not to mention the benefits of using reselect to do some memoizations.
For this task you can use the Array.prototype.find():
const myData = [{
name: 'some name',
id: 1
}, {
name: 'some name2',
id: 2
}, {
name: 'some name3',
id: 3
}, {
name: 'some name4',
id: 4
}]
const filterProductsByCategoryId = (state, id) => {
return state.find(c => c.id === id);
};
const result = filterProductsByCategoryId(myData, 2);
console.log(result);