Redux action property defaulting to 'undefined' rather than an empty array - arrays

I have noticed that when setting and initial state action object in redux, that empty arrays return undefined, which is keeping me from accessing array methods like .push() or .length.
Is there a way to stop the value from simplifying down to undefined?
export const startAddCustomer = (customerData = {}) => {
return (dispatch) => {
const {
name = '',
age = 0,
address = {
address: '',
city: '',
state: ''
},
coats = [],
shoes = [],
phoneNumber = '',
dependants = [],
visits = [],
} = customerData;
const customer = {
name,
age,
address,
coats,
shoes,
phoneNumber,
dependants,
visits
};
database.ref('customers').push(customer).then((ref) => {
dispatch(addCustomer({
id: ref.key,
...customer
}))
})
}
};

Related

React JS Unhandled Rejection (TypeError): item.persistVariablesLimit.trim is not a function

I'm getting the following error:
React JS Unhandled Rejection (TypeError): item.persistVariablesLimit.trim is not a function
when I try to set a default value on the persistVariablesLimit.
Here is an image of the stack trace and console log:
Here's the code that generates it:
if (typeof item.persistVariablesLimit !== 'undefined') {
item.persistVariablesLimit = item.persistVariablesLimit.trim()
if (!item.persistVariablesLimit.match(/^[0-9]+$/)) {
formIsValid = false
errors['persistVariablesLimit'] = 'Must be numeric'
}
}
There is no problem with this field if the data is entered manually, even if it is entered, then deleted.
Also, if I don't set a default value, and don't enter anything into the field, it is saved successfully as an empty string.
Here's where it sets the default value (when the "FA" template is selected):
handleSelectTemplateChange = (event, { value }) => {
let item = { ...this.state.item }
item.template = value
var str1 = '' + item.template
if (str1.startsWith('SMA')) {
item.family = 'Safety-Machine-Analytics'
}
if (str1.startsWith('FA')) {
item.family = 'Field Analytics'
item.product = 'Field Analytics'
if (!item.persistVariablesLimit) {
item.persistVariablesLimit = 50;
}
if (!item.dataSourceLimit) {
item.dataSourceLimit = 50;
}
}
else {
item.persistVariablesLimit = "";
item.dataSourceLimit = "";
}
this.setState({ item })
}
This is the UI code for the template:
<Form.Select
fluid
label='Template'
options={this.state.templateOptions || []}
placeholder='Template'
name='template'
value={item.template}
required={true}
onChange={this.handleSelectTemplateChange}
/>
And for the persistVariableLimit field:
<Form.Input
label='Persist Variables Limit'
placeholder='Persist Variables Limit'
name='persistVariablesLimit'
value={item.persistVariablesLimit || ''}
onChange={this.handleChange}
required={false}
disabled={false}
error={this.state.errors['persistVariablesLimit']}
/>
This is an item originally retrieved from an API. It's initialized as follows:
emptyItem = {
fullName: '',
contact: '',
requester: '',
tag: '',
company: '',
companyId: '',
product: '',
expiration: '',
macsArray: '',
dashboardIDs: '',
typeId: '',
family: '',
systems: '',
fileName: '',
url: '',
attributesArray: [],
persistVariablesLimit: '',
dataSourceLimit: ''
}
constructor(props) {
super(props)
const { cookies } = props
this.state = {
item: this.emptyItem,
csrfToken: cookies.get('XSRF-TOKEN'),
fields: {},
errors: {}
}
...
}
Here's the API call:
if (this.props.match.params.id !== 'new') {
try {
const tmpLicense = await (await fetch(API_HOST + `/api/license/${this.props.match.params.id}`, { credentials: 'include' })).json()
this.setState({ item: tmpLicense })
} catch (error) {
this.props.history.push('/')
}
How should I be setting this default value? What is the issue here?
You’re setting the default values as numbers, .trim is a String method.
It should be:
if (!item.persistVariablesLimit) {
item.persistVariablesLimit = '50';
}
if (!item.dataSourceLimit) {
item.dataSourceLimit = '50';
}

TypeError: Cannot assign to read only property 'item' of object '#<Object>'

I'm having this issue for a days, here is my current state structure:
const [inputFields, setInputFields] = useState([
{
item: '',
quantityIssued: 0,
quantityRequested: '',
remarks: '',
unit: '',
},
])
When I click the edit button I need to fill the date to my state eg.
const editHandler = (order) => {
const custom = [
{
item: 'test',
quantityIssued: 0,
quantityRequested: 7,
remarks: '8',
unit: '1',
},
{
item: 'test2',
quantityIssued: 0,
quantityRequested: 7,
remarks: '8',
unit: '1',
},
]
setInputFields(custom)
}
When I use that custom data I am able to edit the data of my state but when I try to fetch that data from my server which is the same structure I'll get an error eg:
const editHandler = (order) => {
setInputFields(order.orderItems)
}
although they are the same data which I showed you above, I can't edit if I edit it says that error title can not assign read-only property
this is my interface:
Alhamdulillah! finally found the solution after days from this answer:
https://stackoverflow.com/a/60960285/12463240
In the handleInputChange I have to do like this:
const handleInputChange = (e, index) => {
const { name, value } = e.target
const old = inputFields[index]
const updated = { ...old, [name]: value }
var list = [...inputFields]
list[index] = updated
setInputFields(list)
}
Before It was like this:
const handleInputChange = (e, index) => {
const { name, value } = e.target
var list = [...inputFields]
list[index][name] = value
setInputFields(list)
}

React state manipulation through spread operator

I have this initial state:
this.state = {
formInfo: {
name: '',
age: '',
height: ''
},
errorMessage: ''
}
When editing the form, the state should update and this is how I'm handling this at the moment:
handleInputChange = e => {
const { name, value } = e.target
this.setState({
...this.state,
formInfo: {
...this.state.formInfo,
[name]: value
}
})
}
Can you provide me of a better solution to manipulate the state, in case this process does not follow the industry standards?
I doubt if there's a more efficient way to archive this.
If you are asking about best practice then below is the one. Your code was fine. Only better to avoid dot notation's and provide default values.
handleInputChange = e => {
const {
target: {
name = '',
value = '',
} = {},
} = e;
const {
formInfo,
} = this.state;
this.setState({
formInfo: {
...formInfo,
[name]: value,
},
});
}

Should I check if array is null in a react selector

Story
I am working on a react application with a state redux.
In that state, there is an array called 'students', initialized as empty. This array is updated through calls to HTTP rest services.
I have also created a selector to pick the data that I need.
student.reducer.ts :
const initialState = {
students: []
}
student.selector.ts :
export const studentsFullName = (students) => {
return students.map(
student => ({
id: student.id,
fullName:
`${student.firstName} ${student.lastName}`
})
);
};
Question
Considering that the rest service should NOT return a null object for the students, should I, in the selector, add a condition to return an empty array if 'students' is null, or is it useless ?
student.selector.ts :
export const studentsFullName = (students) => {
// is this necessary ?
if (!students){
return [];
}
return students.map(
student => ({
id: student.id,
fullName:
`${student.firstName} ${student.lastName}`
})
);
};
You can have a check like
export const studentsFullName = (students) => {
return {student.length && students.map(
student => ({
id: student.id,
fullName:
`${student.firstName} ${student.lastName}`
})
);
}
};
Thank you for your replies. Finally, I used #Zohaib solution, which is
(students || []).map(
student => ({
id: student.id,
fullName:
`${student.firstName} ${student.lastName}`
})
);

Filter products depend on another ACTION in React-native Redux

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);

Resources