React filter payload arrays - arrays

I need help filtering an array of different values.Im using React. Thank you in advance!!!
I have a payload that contains 3 different types of arrays, each type of array has the following key values.
myPayload : array(3)
0: {name: “John “ , age: “24” , height: “174cm” , **type:
“student”**}.
1: {name: “Jane” , age: “49” , height: “168cm” ,**type: “teacher”**}
2: { name: “schoolName” , country: “UnitedStated” , state: “IL”,
**type:”building”**}
Cards of type "student" & "teacher" contain the same fields but the "building" card has different fields from the others.
I am rendering the arrays as cards and my goal is to be able to search through all values of the arrays/cards.
This is what I have in my cards.reducer
import { FILTER_CARDS, FETCH_CARDS } from '../X/Y';
const initialState = { myPayload: [], filteredPayload: [] };
export default function (state = initialState, action) {
const { type, myPayload = [], value } = action;
const handler = {
[FETCH_CARDS]: {
...state,
myPayload,
},
[FILTER_CARDS]: {
...state,
filteredPayload: state.myPayload.filter(({ name }) => name.toLowerCase().match(value)),
},
};
return handler[type] || state;
}
The above only filters through the "name" field of each card.
I tried filtering through all values (name, age, height, type, schoolName, country, state) by making changes to "FILTER_CARDS" but that breaks and gives me an error of
"cannot read property 'toLowerCase' of undefined"
[FILTER_CARDS]: {
...state,
filteredPayload: (state.myPayload.type == ('student' || 'teacher')
? (state.myPayload.filter(({ name }) => name.toLowerCase().match(value)),
state.myPayload.filter(({ age }) => age.toLowerCase().match(value)),
state.myPayload.filter(({ height }) => height.toLowerCase().match(value)))
: (state.myPayload.filter(({ name }) => name.toLowerCase().match(value)),
state.myPayload.filter(({ country }) => country.toLowerCase().match(value)),
state.assetsPayload.filter(({ state }) => state.toLowerCase().match(value)))),
},
any input on how to filter through all values in this scenario?

You could just search in the values of the object, so you do not really need to care about the key names.
filteredPayload: state.myPayload.filter(item => (
Object.values(item).some(objValue => (
objValue.toLowerCase().match(value)
))
))

I solved this by doing the following
filteredPayload: state. myPayload.filter(item => {
switch (item.type) {
case 'building':
return (
item.name.toLowerCase().match(value)
|| item.country.toLowerCase().match(value)
|| item.state.toLowerCase().match(value)
);
case 'student':
case 'teacher':
return (
item.name.toLowerCase().match(value)
|| item.age.toLowerCase().match(value)
|| item.height.toLowerCase().match(value)
);
}
return item;
}),
Thank you all for helping out ^_^

Related

React Redux Error the page is displayed before getting the data

I have a problem. I would like to get some data before loading the DOM but I have been running into this error for hours. I have a 200 response on my query but the error persists. After a reload of the page the display is ok.
// redux
const dispatch = useDispatch();
const customers = useSelector((state) => state.customerReducer);
useEffect( () => {
dispatch(findAllCustomers());
}, [])
{ !isEmpty(Object.values(customers)) && Object.values(customers)[0].map((customer, index) => ...
Uncaught TypeError: Object.values(...)[0].map is not a function ...
Thanks for your help.
[Array(54)]
0: (54) [{…}, {…}, {…}, {…},
0: Array(54)
0: {id: 2,, …}
1: {id: 3, …}
2: {id: 4 , …}
//Actions.js
export const findAllCustomers = () => {
return (dispatch) => {
axios.get('/api/customers')
.then((response) => {
dispatch({
type: FIND_ALL_CUSTOMERS, payload:response.data
})
})
.catch((error) => console.log(error.response))
}
}
//CustomersReducer.js
const INITIAL_STATE = [];
function customerReducer(state = INITIAL_STATE, action)
{
switch (action.type){
case 'FIND_NB_CUSTOMERS' : {
return {
...state,
nbCustomers : action.payload
}
}
case 'FIND_ALL_CUSTOMERS' : {
return {
...state,
customers: action.payload
}
}
default:
return state
}
}
export default customerReducer;
//isEmpty()
export const isEmpty = (value) => {
console.log(value)
return (
value === undefined ||
value === null ||
(typeof value === "object" && Object.keys(value).length ===
0) ||
(typeof value === "string" && value.trim().length === 0)
);
}
Object.values(customers) return an Array and you are trying to access the first index of that array which is probably not an Array anymore.
Change to:
Object.values(customers).map()
The problem is that you are trying to use map() on something that isn't an array. Object.values(customers)[0] is an object. However, since customers is an array, there is no reason to use Object.values() at all. Instead, just map over the array directly with customers.map().
So all together it should be
{ customers && customers.map(...) }

add inputs dynamically react redux

hello I will ask this question again because I still can’t find a answer
I try to create a form similar to google form with react and redux
each question is represented by: QuestionCard
contains a title and can have several types (short question, long question, choice ,multiple, single choice ...)
I manage to add cardQuestion and delete them
my problem is that when I select single choice I want to give the user the possibility to add the number of choices he wants but it does not work
this is my reducer
const initialState = {
formId: '',
form: {
title: '',
description: ''
},
basicForm: {
fullName: '',
age: '',
saved: false
},
cardQuestion: [{
title: '',
choix: [],
type: '',
}],
error: "",
loading: false,
}
const addRequestReducer = (state = initialState, action) => {
switch (action.type) {
case 'SET_STATE':
return { ...state, ...action.payload }
default:
return state
}
}
i use a global state
and this how i try to add choices
1- i handle change of inputs (each choice) like this :
const { cardQuestion } = useSelector((state) => state.addRequest)
const choice = cardQuestion.map(e => e.choix)
const onChangeInput = (i, key, value) => {
console.log(value)
dispatch({
type: 'SET_STATE',
payload: {
cardQuestion: cardQuestion.map((elem, index) => {
console.log(i)
console.log(i === index)
if (i !== index) return elem
return { ...elem, [`${key}`]: value }
})
}
})
}
2- use the ChangeInptut like this
<div >{choice.map((elem, i) => <div key={i}>
<Input value={elem.choix} onChange={(e) => onChangeInput(i, 'choix', e.target.value)} />
</div>
3- button to add new input (choice)
<Button onClick={() => dispatch({
type: 'SET_STATE',
payload: {
cardQuestion: [...cardQuestion, { choix: '' }]
}
})} >Add</Button>
</div>
but it give me results like this :
and when i add a choice in question card1 it is also added in questioncard2
how can i resolve this , any help will be appreciated
This
cardQuestion: [...cardQuestion, { choix: '' }]
adds a new card without a title or type to the list of cards. If I understand you correctly, you want to add a new choice to an existing card, which will look something like
const newCards = [...cardQuestion]
newCards[currentCardPosition] = {...newCards[currentCardPosition]}
newCards[currentCardPosition].choix = [...newCards[currentCardPosition].choix, '' ]
but I don't see where currentCardPosition will come from

Delete multiple item from array - Redux State

I'm working on react app with redux. I want to delete multiple item from array. I write below code in my reducer which delete single item from array but i want to delete multiple item.
case DELETE_LINK:
let dltLink = state.filter(item => {
return item._id !== action.data._id
})
return {
...state,
parentFolderlinks: dltLink
};
It seems you want to filter links from state.parentFolderlinks, say you have the ids in action.data.ids, you could
case DELETE_LINK:
const parentFolderlinks = state.parentFolderlinks.filter(item => {
return !action.data.ids.includes(item._id);
});
return {
...state,
parentFolderlinks
};
On what basis would you like to filter items? I assume that multiple items will not have the same id.
Below example shows how we can filter multiple items in redux. In this case, foods state with items that has type as fruit and removes everything else.
// initial state with all types of foods
const initialState = {
"foods": [
{
name: "apple",
type: "fruit"
},
{
name: "orange",
type: "fruit"
},
{
name: "broccoli",
type: "vegetable"
},
{
name: "spinach",
type: "vegetable"
},
]
}
// sample reducer that shows how to delete multiple items
export default (state = initialState, { type, payload }) => {
switch (type) {
// delete multiple items that does not have type fruit
// i.e both brocolli and spinach are removed because they have type vegetable
case DELETE_ITEMS_WITHOUT_TYPE_FRUIT:
const onlyFruits = state.foods.filter(food => food.type === "fruit");
return {
...state,
foods: onlyFruits
}
}
}
you could map over the state and run it through a function that works out if you want to keep it or not (I don't know what your logic is for that) then return the array at the end
const keepThisItem =(item) => {
return item.keep
}
case DELETE_LINK:
let itemsToKeep = []
let dltLink = state.map(item => {
if(keepThisItem(item){
itemsToKeep.push(item)
}
return itemsToKeep
})

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

Overwriting array of objects instead of updating it

Im currently learning redux and trying few options out. Everything looks okay until I want to update one state in the array of objects.
I'm dispatching 5 actions in total now, first 2 setting longitutde and latitude for one part of the state using one reducer, then I set IDs with other reducer and finally when I want to update one of the objects in the array I update one but delete the other somehow.
My file looks like this:
const demoState = {
renderedDrugs: [
{
id: '',
drugId: '',
price: undefined
}
],
apt: {
latA: '',
lonA: ''
}
}
//SET_ID
const setId = (id, drugId) => ({
type: 'SET_ID',
renderedDrugs: {
id,
drugId
}
})
//SET_PRICE
const setPrice = (drugId, price) => ({
type: 'SET_PRICE',
drugId,
price
})
//RENDERED DRUGS REDUCER
const renderedDrugsReducerDefState = [];
const renderedDrugsReducer = (state = renderedDrugsReducerDefState, action) => {
switch (action.type) {
case 'SET_ID':
return [
...state,
action.renderedDrugs
]
case 'SET_PRICE':
return state.map((drug) => {
if (drug.drugId === action.drugId) {
return {
...drug,
...action.price
}
}
})
default:
return state;
}
}
//SET_LAT
const setLat = (latA) => ({
type: 'SET_LAT',
latA
})
//SET_LON
const setLon = (lonA) => ({
type: 'SET_LON',
lonA
})
//APT REDUER
const aptReducerDefState = []
const aptReducer = (state = aptReducerDefState, action) => {
switch (action.type) {
case 'SET_LAT':
return {
...state,
latA: action.latA
}
case 'SET_LON':
return {
...state,
lonA: action.lonA
}
default:
return state;
}
}
//STORE CREATION
const store = createStore(
combineReducers({
renderedDrugs: renderedDrugsReducer,
apt: aptReducer
})
)
store.subscribe(() => {
console.log('store', store.getState());
})
store.dispatch(setLat(88));
store.dispatch(setLon(78));
store.dispatch(setId(uuid(), 3));
store.dispatch(setId(uuid(), 35));
store.dispatch(setPrice(35, {price: 400}));
I assume the SET_PRICE action is at fault, but I tried various configurations and cant figure out the issue so thats why I posted the whole file, if thats unnecessary let me know and I will delete irrelevant bits.
Console log after 4th dispatch:
{renderedDrugs: Array(2), apt: {…}}
apt
:
{latA: 88, lonA: 78}
renderedDrugs
:
Array(2)
0
:
{id: "2a3c4bca-610a-4554-b7e3-695ae6e30ae7", drugId: 3}
1
:
{id: "48df057a-c8f1-4138-8db7-6268f7508ccb", drugId: 35}
length
:
2
__proto__
:
Array(0)
__proto__
:
Object
and aftr 5th
{renderedDrugs: Array(2), apt: {…}}
apt
:
{latA: 88, lonA: 78}
renderedDrugs
:
Array(2)
0
:
undefined
1
:
{id: "48df057a-c8f1-4138-8db7-6268f7508ccb", drugId: 35, price: 400}
length
:
2
__proto__
:
Array(0)
__proto__
:
Object
The .map doesn't return the unchanged objects for the items your're not updating. Adding a return should fix it:
return state.map((drug) => {
if (drug.drugId === action.drugId) {
return {
...drug,
...action.price
}
}
return drug; // this was missing before, so the return was undefined
})

Resources