How can I check for null when destructuring my data:
const {
vehicles: {
data: { reminderVehicles },
},
} = useSelector((state) => state);
The below code gives me
null is not an object, evaluating vehicles.data.reminderVehicles
document.getElementById("VehicleRegistration").value = "${
(prepopVehicleReg && reminderVehicles[0]?.Registration) || ""
}";
You can always assign a default value to it.
const myData = {
one: 1,
two: 2,
nested: {
three: 1
}
};
const {one = null} = myData;
const {five = null} = myData;
const {nested: {three}} = myData;
const {nested: {six = 'Default'}} = myData;
console.log({
one,
three,
five,
six
});
In your case, it should be (assuming reminderVehicles is an array)
const {
vehicles: {
data: {
reminderVehicles
} = {
reminderVehicles: []
}
} = {}
} = useSelector((state) => state);
But this is not readable and looks very complicated
Related
What causes the ff issue ? Cannot assign to read only property '0' of object '[object Array]' ?
Any idea would be appreacited. Thanks.
#ts code snippet
const [RegionalList, setRegionalList] = useState<IRegionalList[]>(RegionalListData);
const setEmailValue = (event: any, regionalId: number, index: number) => {
setRegionalList((prevState: IRegionalList[]) => {
const newState = prevState.map((prop: IRegionalList) => {
if (prop.id === regionalId) {
prop.emails[index] = { emailAddress: event.target.value, id: null };
return { ...prop };
}
return prop;
});
return newState;
});
}
here is the way that I suggest :
const [RegionalList, setRegionalList] = useState<IRegionalList[]>(RegionalListData);
const setEmailValue = (event: any, regionalId: number, index: number) => {
setRegionalList((prevState: IRegionalList[]) => {
const newState = prevState.map((prop: IRegionalList) => {
if (prop.id === regionalId) {
return { ...prop,emails:[...prop.emails.filter( (_,i)=>i !== index ),{ emailAddress: event.target.value, id: null }] }
}
return prop;
});
return newState;
});
}
and if you care about order of your list you can do this :
const setEmailValue = (event: any, regionalId: number, index: number) => {
setRegionalList((prevState: IRegionalList[]) => {
const newState = prevState.map((prop: IRegionalList) => {
if (prop.id === regionalId) {
let emails = prop.emails;
emails[index] = { emailAddress: event.target.value, id: null };
return { ...prop,emails }
}
return prop;
});
return newState;
});
}
please let me know if it fixed your problem
I've got the following data structure stored in a useState hook.
const [data, setData] = useState([
{
id: uniqid(),
title: "",
content:[
id: uniqid(),
title: "",
]
},
{
id: uniqid(),
title: "",
content:[
id: uniqid(),
title: "",
],
}
])
I've got a button where the user can add something to the content array, and I'm calling handleReport as below -
const handleAddReport = uniqueID =>{
const object = {
id: uniqid(),
title:"",
}
const formData = [...data];
formData.map(section=>{
section.content.map(report=>{
if(report.id === uniqueID){
section.content.push(object);
};
});
});
setForm(formData);
}
However, this isn't changing the form data at all. I'm not exactly sure how I could get it to work, any help would be appreciated! Thanks
you are not returning anything from the map.
const handleAddReport = uniqueID =>{
const object = {
id: uniqid(),
title:"",
}
const formData = [...data];
const newData = formData.map(section=> {
if(section.id === uniqueID){
section.content.push(object);
}
return section;
});
setForm(newData);
}
But instead of comparing the unqiueId another approach would be to pass the index of your data array. So that we can avoid map .
const handleAddReport = dataIndex =>{
const object = {
id: uniqid(),
title:"",
}
// deep clone the data
const clonedData = JSON.parse(JSON.stringify(data));
// since the data is cloned you can mutate it directly
clonedData[dataIndex].content.push(object);
setForm(clonedData)
}
Try this way
const onAddReport = (uniqid: number) => {
const obj = {
id: 3,
title: ''
};
const formData = [...data];
const mappedData = formData.map((data) => data.id === uniqid ? ({ ...data, content: [...data.content, obj] }) : data)
setData(mappedData);
}
This is my initial data
const data = [
{ id: '1', name: '1' },
{ id: '2', name: '1' },
{ id: '3', name: '2' },
]
I want to loop over and:
Where it has name 1 add that object to stateOne
Where it has name 2 add that object to stateTwo
End goal both states needs to have Array of Objects inside:
stateOne needs to look like
[
{ id: '1', name: '1' },
{ id: '2', name: '1' }
]
stateTwo needs to look like
[
{ id: '3', name: '2' },
]
This is what i've tried:
const data = [
{ id: '1', name: '1' },
{ id: '2', name: '1' },
{ id: '3', name: '2' },
]
const Testing = () => {
const [stateOne, setStateOne] = useState([])
const [stateTwo, setStateTwo] = useState([])
useEffect(() => {
data.forEach((e) => {
if (e.name === '1') {
console.log('e', e)
setStateOne((prevSate) => ({ ...prevSate, e }))
}
// if (e.name === '2') {
// setStateTwo(e)
// }
})
}, [])
console.log('stateOne', stateOne)
}
I'd prefer sending data as a prop to that component
You can achieve what you need by
const data = [
{ id: '1', name: '1' },
{ id: '2', name: '1' },
{ id: '3', name: '2' },
]
export default function Testing() {
const [stateOne, setStateOne] = useState([])
const [stateTwo, setStateTwo] = useState([])
useEffect(() => {
setStateOne(data.filter(e => e.name === "1"))
setStateTwo(data.filter(e => e.name === "2"))
console.log('stateOne', stateOne)
console.log('stateTwo', stateTwo)
}, [])
}
setState functions as an assignment. Like you would normally assign a variable. That means if you want to add something to an array, you need to include that array in the assignment.
Something like this:
if (e.name === '1') {
console.log('e', e)
setStateOne([...stateOne, e])
}
if (e.name === '2') {
setStateTwo([...stateTwo, e])
}
If you don't want to use filter twice for whatever reason, You can create temporary array for each one and manipulate them then update each state respectively like so:
const [stateOne, setStateOne] = useState([]);
const [stateTwo, setStateTwo] = useState([]);
useEffect(() => {
const tempArr1 = [];
const tempArr2 = [];
data.forEach((item) => {
if (item.name === "1") {
tempArr1.push(item);
} else if (item.name === "2") {
tempArr2.push(item);
}
});
setStateOne(tempArr1);
setStateTwo(tempArr2);
}, []);
console.log(stateOne);
console.log(stateTwo);
The problem with what you're doing is you're updating the state each time you find a match which will cause a lot of unnecessary re-renders.
You've said that data comes from some API you're querying. If so, filter the data once you get it. You can do that in a couple of ways.
With two calls to filter:
const Testing = () => {
const [stateOne, setStateOne] = useState([]);
const [stateTwo, setStateTwo] = useState([]);
useEffect(() => {
let cancelled = false;
getTheData(data => {
if (cancelled) {
return;
}
setStateOne(data.filter(({name}) => name === "1"));
setStateTwo(data.filter(({name}) => name === "2"));
};
return () => {
// So we don't try to set setate on an unmounted component
cancelled = true;
};
}, []);
// ...use `dataOne` and `dataTwo` here...
};
Or if you don't want to make two passes through the data, a single loop:
const Testing = () => {
const [stateOne, setStateOne] = useState([]);
const [stateTwo, setStateTwo] = useState([]);
useEffect(() => {
let cancelled = false;
getTheData(data => {
if (cancelled) {
return;
}
const stateOne = [];
const stateTwo = [];
for (const entry of data) {
switch (entry.name) {
case "1":
stateOne.push(entry);
break;
case "2": // or default if you want all non-1s in `stateTwo`
stateTwo.push(entry);
break;
}
}
setStateOne(stateOne);
setStateTwo(stateTwo);
};
return () => {
// So we don't try to set setate on an unmounted component
cancelled = true;
};
}, []);
// ...use `dataOne` and `dataTwo` here...
};
const data = [
{ id: "1", name: "1" },
{ id: "2", name: "1" },
{ id: "3", name: "2" }
];
const App = () => {
const newdata = useState(data);
const [stateOne, setStateOne] = useState([]);
const [stateTwo, setStateTwo] = useState([]);
const Filter = () => {
let listOne = [];
let listTwo = [];
newdata[0].map((it) => {
if (it.name === "1"){
listOne.push(it);
}
else if(it.name === "2"){
listTwo.push(it)
}
});
setStateOne(listOne);
setStateTwo(listTwo);
};
useEffect(() => {
Filter();
}, []);
console.log("stateOne", stateOne)
console.log("stateTwo", stateTwo)
return (
// your code
)
};
I have a json object with objects inside of it
such as user: {"name": "tim"} and would like a way to turn that in "user.name": 'tim'
I've tried: Javascript Recursion normalize JSON data
Which does not return the result I want, also tried some packages, no luck
You can use a recursive approach to flatten nested objects, by concatenating their keys, as shown below:
const flattenObject = (obj) => {
const flatObject = {};
Object.keys(obj).forEach((key) => {
const value = obj[key];
if (typeof value === 'object') {
const flatNestedObject = flattenObject(value);
Object.keys(flatNestedObject).forEach((nestedKey) => {
flatObject[`${key}.${nestedKey}`] = flatNestedObject[nestedKey];
});
} else {
flatObject[key] = value;
}
});
return flatObject;
};
const obj = {
user: { name: 'tim' },
};
console.log(flattenObject(obj));
This solution works for any amount of levels.
If your environment does not support Object.keys, you can use for..in instead:
const flattenObject = (obj) => {
const flatObject = {};
for (const key in obj) {
if (!obj.hasOwnProperty(key)) continue;
const value = obj[key];
if (typeof value === 'object') {
const flatNestedObject = flattenObject(value);
for (const nestedKey in flatNestedObject) {
if (!flatNestedObject.hasOwnProperty(nestedKey)) continue;
flatObject[`${key}.${nestedKey}`] = flatNestedObject[nestedKey];
}
} else {
flatObject[key] = value;
}
}
return flatObject;
};
const obj = {
user: { name: 'tim' },
};
console.log(flattenObject(obj));
This can easily be done like so:
const myObject = {
user: {
firstname: "john",
lastname: "doe"
}
}
function normalize(suppliedObject = {}) {
const newObject = {};
for (const key of Object.keys(suppliedObject)) {
for (const subKey of Object.keys(suppliedObject[key])) {
newObject[`${key}.${subKey}`] = suppliedObject[key][subKey];
}
}
return newObject;
}
console.log(normalize(myObject));
Be aware that this only normalizes 1 level deep. You can extend the function to normalize all the way down to the last level.
Here I validate if my users status is true, and if they are, I put them in an array. The thing here is that next time it will validate, all those who already was true will be added to the same array. Can it be solved by filter instead of push, or should I take the validation in any other way?
import {
UPDATE_LIST_SUCCESS
} from './types'
var arr = []
export const fetchList = () => {
return (dispatch) => {
firebaseRef.database().ref().child('users')
.on('value', snapshot => {
snapshot.forEach(function (child) {
var data = child.val()
if (child.val().profile.status === true) {
arr.push(data)
}
})
dispatch({ type: UPDATE_LIST_SUCCESS, payload: arr })
})
}
}
You can do it like this:
import {
UPDATE_LIST_SUCCESS
} from './types'
export const fetchList = () => {
return (dispatch) => {
firebaseRef.database().ref().child('users')
.on('value', snapshot => {
var arr = snapshot.filter(function (child) {
return child.val().profile.status === true
}).map(function (child) {
return child.val();
});
dispatch({ type: UPDATE_LIST_SUCCESS, payload: arr })
})
}
}
So here is my not so pretty way of solving it, but it works.
import {firebaseRef} from '../firebase/firebase'
import {
UPDATE_LIST_SUCCESS
} from './types'
export const fetchList = () => {
return (dispatch) => {
const arrayToFilter = []
firebaseRef.database().ref().child('users')
.on('value', snapshot => {
let snap = snapshot.val()
// Get acces to the keys in the object i got from firebase
let keys = Object.keys(snap)
// iterate the keys and put them in an User object
for (var i = 0; i < keys.length; i++) {
let k = keys[i]
let name = snap[k].profile.name
let age = snap[k].profile.age
let status = snap[k].profile.status
let profile_picture = snap[k].profile.profile_picture
let users = {name: '', age: '', status: Boolean, profile_picture: ''}
users.name = name
users.age = age
users.status = status
users.profile_picture = profile_picture
// adding the user object to an array
arrayToFilter.push(users)
}
// filter and creates a new array with users depending if their status is true
let arr = arrayToFilter.filter(child => child.status === true)
dispatch({ type: UPDATE_LIST_SUCCESS, payload: arr })
})
}
}