ReactJS: Check if array contains value else append - arrays

I'm trying to check if a JSON response contains a value already inside an array and if it doesn't add it in. The problem I'm having is understanding how to approach this in reactjs. I'm checking before I append it but it doesn't want to work. I've tried passing in user object & user.id but these fail. The attempt below fails to compile but it should help understand what I'm trying to achieve.
Code:
componentWillMount() {
fetch('http://localhost:8090/v1/users')
.then(results => {
return results.json();
})
.then(data => {
data.map((user) => (
if(userList.hasOwnProperty(user.id)) {
userList.push({label: user.title, value: user.id})))
}
})
}

map return the resultant array, but you are not returning anything from it, you should instead use forEach Also you need to check if the userList array contains the id, for that you can use findIndex
What you need is
state = {
userList: [];
}
componentDidMount() {
fetch('http://localhost:8090/v1/users')
.then(results => {
return results.json();
})
.then(data => {
const newUserList = [...this.state.userList];
data.forEach((user) => { // use { here instead of
if(userList.findIndex(item => item.value === user.id) < 0) {
newData.push({label: user.title, value: user.id})
}
})
this.setState({userList: newUserList});
});
}
render() {
return (
{/* map over userList state and render it here */}
)
}

I'd recommend using reduce to turn the returned data into an array you'd like, then adding those values to your existing user list:
fetch('http://localhost:8090/v1/users')
.then(res => res.json())
.then(data => data.reduce((acc, user) => {
const idList = userList.map(user => user.id);
if (idList.indexOf(user.id) === -1) {
acc.push({label: user.title, value: user.id})
}
return acc;
},[]))
.then(newList => userList = [...userList, ...newList]);

Related

getDownloadURL in array of dictionary (re-rendering issue, forEach)

I have an array of dictionaries, (e.g. [{}, {}, {}, {}], each dictionary contains information about book)
I want to download image from firebase storage using getDownloadURL.
My current code's like...
const [resObj, setresObj] = useState() // empty variable for update state
let result = [] //create empty array for copy & push new obj
useEffect(() => {
props.resObj.forEach((obj) => { // props.resObj: array of dictionary I explained before
const jpgName = 'bookDB/'+ obj.도서번호 + '.jpg';
const imgRef = ref(storage, jpgName)
getDownloadURL(imgRef)
.then((url) => {
result1.push({
...obj,
bookUrl: url
}) // copy & push dictionary
})
.catch((error) => {
if (error.code === 'storage/object-not-found') {
console.log('이미지 파일 없음')
result1.push({
...obj,
bookUrl: "https://upload.wikimedia.org/wikipedia/commons/a/ac/No_image_available.svg"
})
} else { console.log(error)}
})
})
setresObj(result1)
}, [])
after this code update 'resObj' variable,
I map resObj in component like...
return (
<div>
{resObj? resObj.map(item => {
<img
key = {}
className = '~~'
onClick = {}
src = {item.bookUrl}
/>
})}
</div>
)
unfortunately.. it doesn't show nothing..
It seems that forEach, useEffect, useState, getDownloadURL Promise seriously entangled..
I tried 1) devide download image code as function, 2) devide download image code as recoil, 3) escape download image code from useEffect, 4) ...(extra variances of code)...
The problem is that your call to setresObj happens before any of the calls to result1.push have happened, so you're always setting an empty array. It's easiest to verify this by setting breakpoints and running in the debugger, or by adding some console.log calls.
The fix is to use Promise.all to wait for all download URLs to have been retrieved and only then call setresObj. Something like this:
useEffect(() => {
let promises = props.resObj.map((obj) => {
const jpgName = 'bookDB/'+ obj.도서번호 + '.jpg';
const imgRef = ref(storage, jpgName)
return getDownloadURL(imgRef)
.then((url) => {
return {
...obj,
bookUrl: url
}
})
.catch((error) => {
if (error.code === 'storage/object-not-found') {
console.log('이미지 파일 없음')
result1.push({
...obj,
bookUrl: "https://upload.wikimedia.org/wikipedia/commons/a/ac/No_image_available.svg"
})
} else { console.log(error)}
})
})
Promise.all(promises).then((results) => {
setresObj(results);
});
}, [])

How to use "if" inside useState prevState map

Does anybody know how can i use if statement like this.
This example doesnt work
uppy.on('complete', (result) => {
result.successful.forEach((file) =>
setImgs((prevState) =>
prevState.map((item) => {
if(item.id === file.id) {
return {
...item,
image: file.preview
}
}
})
)
)
})
And this works, but there s no if
uppy.on('complete', (result) => {
result.successful.forEach((file) =>
setImgs((prevState) =>
prevState.map((item) => ({
...item,
image: file.preview,
}))
)
)
})
I don't think you need to map if you're just trying to find an item.
You could do
const item = prevState.find(x.id ==> file.id)
return item? {...item.image:file.preview} : null
"doesn't work" will need more specification. Out of observation I can tell that it needed to have else statement or without, in order to return item if no change is required. The variable - item is unchanged element of imgs array, which we put back.
This is after refactoring your pseudocode:
uppy.on("complete", (result) => {
result.successful.forEach((file) =>
setImgs((prevState) =>
prevState.map((item) => {
if (item.id === file.id) {
return { id: item.id, image: file.preview };
} else return item;
})
)
);
});
Check the sandbox here
Since you are using a map that returns a new array, also you are trying to add an image key to the matched item only then, you need to also return for the else case.
const data = state.map((item) => {
if (item.id === file.id) return { ...item, image: file.preview };
return item;
});

convert nested AngularJS service to Angular Observable service

I have some AngularJS(pre 1.5) services using nested calls in a project that we are rebuilding in Angular(11).
The services use nested calls but I have no idea how to rebuild them using RXJS.
Any help, or detailed links to understand how I can get the result I need would be great.
I have not been able to find anything sofar that helps me understand how to resolve this.
This is the original service:
function getBalanceGroups() {
return $http.get(url.format("/accounts/{{accountId}}/balance-groups", $stateParams))
.then(function (response) {
_.each(response.data, function (item) {
getBalanceViews(item.accountBalanceGroupId)
.then(function (balanceViewData) {
item.balanceViews = _.sortBy(balanceViewData, function (view) {
return (view.balanceViewId === item.totalIndebtednessViewId) ? 0 : 1;
});
_.each(item.balanceViews, function (view) {
getBalanceSegments(view.accountBalanceViewId)
.then(function (balanceSegmentData) {
view.balanceSegments = balanceSegmentData;
view.totalBalance = 0;
view.totalBalance = _.sumBy(view.balanceSegments, "balance");
});
});
});
});
response.data = _.sortBy(response.data, function (item) {
return (item.isActive && item.isPrimary) ? 0 : 1;
});
return new LinkedList(response.data);
}, function (error) {
$log.error('Unable to return balance group for the balanceChiclet');
});
}
This is what I have so far: (not working - it is returning the final api data response, I need to use the data to use the data to modify the previous response and return the modified data. No idea how )
getBalanceGroups(accountId: number | string): Observable<any> {
let balGroupsUrl = `/accounts/${accountId}/balance-groups`;
return this.http.get(`${this.baseUrl}${balGroupsUrl}`).pipe(
mergeMap( (groups: any) => groups),
flatMap((group:any) => {
group.balanceViews = [];
return this.getBalanceViews( group.accountBalanceGroupId, group )
}),
mergeMap( (views: any) => views),
flatMap((views: any) => {
return this.getBalanceSegments( views.accountBalanceViewId )
}),
catchError((err) => of(err) ),
tap( groups => console.log('groups: 3:', groups) ),
)
}
private getBalanceViews(accountBalanceGroupId: number | string, group): Observable<any> {
let balViewsUrl = `/balance-groups/${accountBalanceGroupId}/balance-views`;
return this.http.get(`${this.baseUrl}${balViewsUrl}`);
}
private getBalanceSegments(accountBalanceViewId: number | string): Observable<any> {
let balSegUrl = `/balance-views/${accountBalanceViewId}/balance-segments`;
return this.http.get(`${this.baseUrl}${balSegUrl}`);
}
Instead of the mergeMap + flatMap (they are synonymous BTW), you could use forkJoin to trigger multiple requests in parallel.
You might have to use multiple nested forkJoin given the nature of the request.
While I've converted the loadash sumBy using Array#reduce, I've left the sort incomplete for you to do it.
Try the following
getBalanceGroups(): Observable<any> {
return this.http.get(`/accounts/${accountId}/balance-groups`, { params: stateParams }).pipe(
switchMap((response: any) =>
forkJoin(
response.data.map((item: any) =>
getBalanceViews(item.accountBalanceGroupId, item).pipe(
map((balanceViewData: any) => ({
...item,
balanceViews: balanceViewData.sort() // <-- incomplete
})),
switchMap((item: any) =>
forkJoin(
item.balanceViews.map((view: any) =>
getBalanceSegments(view.accountBalanceGroupId).pipe(
map((balanceSegmentData: any) => ({
...item,
balanceSegments: balanceSegmentData,
totalBalance: view.balanceSegments.reduce((acc, curr) => acc += curr['balance'], 0)
}))
)
)
)
)
)
)
)
),
map((response: any) => ({
...response,
response.data: response.data.sort() // <-- incomplete
})),
catchError((error: any) => {
console.error('Unable to return balance group for the balanceChiclet');
return of(error);
})
);
}

React Native & Flatlist, array items not available (.length === 0)

I'm experience an issue in react native where when the array i have in my state is behaving very strangely. It says i have content in the array, but the array.length === 0. Here is a screenshot of my chatrooms array to show you what i mean (this is at the beginning of my FlatList component (receiving props.chatrooms array)
console.log('props.chatrooms')
console.log(props.chatrooms)
console.log(props.chatrooms.length)
I'm grabbing all the private and group chatIds, then making a call to those chatrooms to get the details of the users. Here is how i'm pulling the information:
componentDidMount() {
this.setState({ isLoading: true })
this.props.navigation.addListener('focus', () => {
//? Refactor to pull userId from Redux Store
let friendsArray = []
let chatrooms = []
let privateChatIds = []
let groupChatIds = []
AsyncStorage.getItem('userId').then(userId => {
let currentUser = {}
firebase.firestore().collection('users').doc(userId).get().then(snapshot => {
currentUser = snapshot.data()
}).then(() => this.setState({ currentUser: currentUser }))
firebase.firestore().collection('users').doc(userId).collection('friends').get().then((snapshot) => {
if (!snapshot.empty) {
snapshot.forEach((doc) => {
friendsArray.push(doc.data())
privateChatIds.push(doc.data().chatroomId)
});
} else {
console.log('Nothing to Grab')
}
}).then(() => this.setState({ friends: friendsArray }))
// get each array and put into the chatroomsArray
firebase.firestore().collection('users').doc(userId).collection('groupChatrooms').get().then((snapshot) => {
if (!snapshot.empty) {
snapshot.forEach((doc) => {
groupChatIds.push(doc.data().key)
})
}
//! will want to limit this number to like 10-15 or something
}).then(() => {
for (let i = 0; i < privateChatIds.length; i++) {
firebase.firestore().collection('chatrooms').doc('private').collection(privateChatIds[i]).doc('info').get().then((snapshot) => {
chatrooms.push(snapshot.data())
})
}
for (let i = 0; i < groupChatIds.length; i++) {
firebase.firestore().collection('chatrooms').doc('group').collection(groupChatIds[i]).doc('info').get().then((snapshot) => {
chatrooms.push(snapshot.data())
})
}
}).then(() => {
console.log('chatrooms')
console.log(chatrooms)
this.setState({ chatrooms, isLoading: false })
})
})
})
}
is my code perfect? no, probably pretty terrible performance-wise, but i'm just trying to get it to work. I figure i'm resetting the chatrooms array or something, but why would it log as an array of items i want but then immediately after log as .length === 0??? Help!
Update: I'm seeing this content render from the flatlist initially and then it disappears

How to take data from array? Can't take data in const from array

This is data array protocols
https://dropmefiles.com/MchK8 this is my code.
Why selectMembers is empty and when I directly do the protocol.Members, it issues data?
How can i use Selectedmembers?
componentDidMount() {
const {members} = this.props;
const selectedMembers = [];
this.getProtocolsDetails(this.props.protocol.id);
this.setState({isLoading: true});
console.log(protocol);
// if (!isNull(protocol.members) && protocol.members.length) {
// protocol.members.map(member => {
// selectedMembers.push({
// label: member.fullName,
// value: member.code
// });
// });
// }
}
getProtocolsDetails(id) {
ProtoApi.getProtocolById(id)
.then(protocol => {
this.setState({protocol}, () => {
});
})
.catch(() => {
});
}
<AutoComplete label="Участники"
placeholder="Выберите участников"
searchApi={this.onSearchMembers}
onUpdate={this.onChooseMember}
onDeleteItem={this.onDeleteMember}
multiple={true}
dataSource={selectedMembers}
error={!isUndefined(errors.members)}
helperText={!isUndefined(errors.members) ? errors.members : null}/>
I want to take and show data when i edit this element members in selectedMembers from array looks like empty but it show like this if i have doesnt empty selectedMembers
The first after request you save data in this.state.protocol, but display in console.log just protocol.
The second componentDidMount will not wait api response, you should use componentDidUpdate or replace logic in getProtocolsDetails

Resources