React - Loop inside a loop not rendering - reactjs

I have a map loop inside another loop like this:
{"undefined" !== typeof this.props.categoryObject['products'] && Object.keys(this.props.categoryObject['products']).length > 0 && Object.keys(this.props.categoryObject['products']).map(keyProd => {
const product = this.props.categoryObject['products'][keyProd];
if("undefined" !== typeof product) {
Object.keys(this.props.activeFilters).map(keyFilter => {
console.warn(this.props.activeFilters[keyFilter]);
return (<div>test</div>)
})
}
})}
The console works, but not the render. Any idea why ?
Thank you

The problem here that outer .map doesn't have return statement and your outer code doesn't have return statement too.
So you have to change your code to the following
return ("undefined" !== typeof this.props.categoryObject['products'] && Object.keys(this.props.categoryObject['products']).length > 0 && Object.keys(this.props.categoryObject['products']).map(keyProd => {
const product = this.props.categoryObject['products'][keyProd];
if("undefined" !== typeof product) {
return Object.keys(this.props.activeFilters).map(keyFilter => {
console.warn(this.props.activeFilters[keyFilter]);
return (<div>test</div>)
})
}
}))
Also some notes on how you can make your code more readable with new es6 features. Commented version
// It's better to extract products into separate variable
// as there are a lot of copy paste code for this action
const { products } = this.props.categoryObject;
// `undefined` is falsy value so you can just test next condition for early exit
if (!products) { return null; }
// 1. no need to test for `.length` as iterating empty array will make 0 iterations
// 2. use Object.entries you can immediately get key and value
return Object.entries(products).map(([key, product]) => {
// Same things as with `products` variable
if (product) {
// I think for your future code you can use `Object.entries` here too
return Object.keys(this.props.activeFilters).map(keyFilter => {
return (<div>test</div>)
})
}
})
Final version:
const { products } = this.props.categoryObject;
if (!products) { return null; }
return Object.entries(products).map(([key, product]) => {
if (product) {
return Object.keys(this.props.activeFilters).map(keyFilter => {
return (<div>test</div>)
})
}
})
NOTE to use all of them in all common browser you have to configure your babel properly

Related

Is there any way to determine which dependency has been changed in useEffect when you have multiple without if else

I am trying to use multiple dependencies inside single useEffect hook instead of using separate useEffect for each constant. I know that it will call everytime something got changed in that constant. But I was wondering if there is a way to check which one is changed without so much if else statement. So that i can just apply the switch case.
Here is my code
useEffect(() => {
setDisabled(true);
setstatusReconciled(true);
if (selectedTransactionsRows.length > 0) {
if (selectedTransactionsRows.length === 1 && selectedTransactionsRows[0].reconciled) {
setstatusReconciled(false);
} else {
const checkReconciledExistence = selectedTransactionsRows.some(({ reconciled }) => reconciled === true);
const checkCreditExistence = selectedTransactionsRows.some(({ type }) => type === 'CR');
const checkDebitExistence = selectedTransactionsRows.some(({ type }) => type === 'DB');
if (
(!checkReconciledExistence && !checkDebitExistence && selectedInvoicesRows.length > 0) ||
(!checkReconciledExistence && !checkCreditExistence && selectedPayoutsRows.length > 0) ||
selectedRefundsRows.length > 0
) {
setDisabled(false);
}
}
}
setErrorMessage(null);
}, [selectedInvoicesRows, selectedTransactionsRows, selectedPayoutsRows, selectedRefundsRows]);
If I take this code into consideration there will be a bunch of if else statement to check all the usecase. I was looking for solution something like this.
useEffect(() => {
setDisabled(true);
setstatusReconciled(true);
if (valueChanged(selectedInvoicesRows)) {
// code for selected invoices
}
if (valueChanged(selectedTransactionsRows)) {
//code for transections
}
if (valueChanged(selectedPayoutsRows)) {
//code for payout
}
if (valueChanged(selectedRefundsRows)) {
//code for refunds
}
setErrorMessage(null);
}, [selectedInvoicesRows, selectedTransactionsRows, selectedPayoutsRows, selectedRefundsRows]);
So that the code looks clean and well managed. I was just checking if it is possible or not.
Thanks for all your help.

CHROME - EXTENSION: Rendered fewer hooks than expected

I am working on a project which creates a web extension. I am checking if my function returns different, I set the new value. Then, I am using the states in return function. However I am getting an error : Rendered fewer hooks than expected. This may be caused by an accidental early return statement. How can I fix that?
const [isSecure, setIsSecure] = useState<boolean>(true)
const [isTabEmpty, setIsTabEmpty] = useState<boolean>(false)
useEffect(() => {
if (hostIsSecure() !== isSecure) {
setIsSecure(hostIsSecure)
}
if (tabEmpty() !== isTabEmpty) {
setIsTabEmpty(tabEmpty)
}
}, [host, tabEmpty, hostIsSecure])
function hostIsSecure(): boolean {
if (protocol && protocol === 'https:') {
return true
} else return false
}
function tabEmpty(): boolean {
if (
curTabId &&
Object.keys(tabHosts).filter(
(id) => curTabId.toString() === id.toString()
).length === 1
)
return false
else return false
}

Find element and render on passing codition

I find names for some objects and have following source code:
const findAndRenderName = (projectId: number| undefined) => {
//i want to render here something when the condtion will pass
projectList?.map(project => projectId = project.id)
}
return (
<DetailsBox title={t('catalogPage.componentDetails.specs.used')}>
{
component?.projects.map(project => findAndRenderName(project.id))
}
</DetailsBox>
);
How to make kind of if from the map function, any idea?
I think what you're trying to accomplish is
projectList?.map(project => {
if(projectId === project.id){
// do something
}
})
of if you refer to the second map
component?.projects.map(project => {
if(findAndRenderName(project.id)){
// do something
}
})

Checking an array for existing array items passed as a parameter

I have 2 react functions below
const hasPermission = (permission: string) => {
if (user?.permissionsEnabled === false) {
return true
}
return permissions.includes(permission) || false
}
The function below accepts an array of a particular type
I want a situation where if any item in the array passed here(parameter) exist in the enum
UserPermissions I want to return a true.
return true does not seem to be working in the method below.
const hasAnyPermission = (permissionsPassed: UserPermissions[]) => {
permissionsPassed.map(permission => {
if (hasPermission(permission)) {
return true
}
})
return false
}
I am calling hasAnyPermission like this..
hasAnyPermission([Edit,View])
Array.prototype.map only returns an array, you need to use Array.prototype.some instead:
const hasAnyPermission = (permissionsPassed: UserPermissions[]) => {
return permissionsPassed.some(permission => {
if (hasPermission(permission)) {
return true
}
})
}
Which is equivolent to:
const hasAnyPermission = (permissionsPassed: UserPermissions[]) => {
return permissionsPassed.some(permission => hasPermission(permission));
}
Or use the regular for-of loops:
const hasAnyPermission = (permissionsPassed: UserPermissions[]) => {
for(let permission of permissionsPassed) {
if (hasPermission(permission)) {
return true
}
}
return false
}
Abbreviated fully-working example:
enum UserPermissions { Edit, View, Delete }
const user = { permissions: [UserPermissions.View] }
console.log("has permission?", hasAnyPermission(user, [UserPermissions.Delete]))
function hasAnyPermission(user, permissions: UserPermissions[]) {
if (user?.permissionsEnabled === false) return true
const userHasPermission = (p) => user.permissions.includes(p)
return permissions.some(userHasPermission)
}

React.JS: Nested Filter/Map Arrays

I'm trying to learn React as part of a course I'm doing with Udacity. I've been battling this issue for a couple of days and can't seem to figure a way out of it.
Basically, I have an API call that returns an array of objects as a promise, I map or filter through these (I've tried both) and need to find the elements that already exist in an array booksOnShelves. I do this by comparing the .id property of each object. Once we find a match I need to set the .shelf property to the same value as the one in the existing Array and if the book doesn't exist I need to set its value to 'none'. All is good here but the problem is when I find a match the property updates the shelf to the correct one, but the iteration continues with the next book that obviously doesn't match and overwrites the .shelf value with none.
Here's the code for this particular method:
class SearchBar extends Component {
state = {
query: '',
result: []
}
getBooks = (query) => {
const booksOnShelves = this.props.books;
BooksAPI.search(query)
.then(res => {
if (res instanceof Array && res.length > 0) {
res.filter( searchedBooks => {
return booksOnShelves.filter( onShelves => {
if ( searchedBooks.id === onShelves.id) {
return searchedBooks.shelf = onShelves.shelf
} else {
return searchedBooks.shelf = 'none
}
})
})
this.setState({result: res.sort(sortBy('title'})
} else {
this.setState({result: []})
}
})
.catch( err => { console.log('ERROR: ', err)})
}
I've tried so many things but none was able to maintain the value of the shelf property.
Is there any way to stop the iteration from overwriting the matched value?
Any ideas how to achieve the intended outcome?
Thank you in advance for the help.
Should work fine in your case:
BooksAPI.search(query).then(res => {
if (res instanceof Array && res.length > 0) {
booksOnShelves.forEach((book) => {
const correspondingRes = res.find((item) => { return (item.id === book.id) });
if (correspondingRes) {
book.shelf = correspondingRes.shelf;
} else {
book.shelf = “none”;
}
});
}
Problem sorted with the help of all you guys I've made it work. Thanks a lot.
Just for reference here's the code that worked.
BooksAPI.search( query )
.then( res => {
if (res instanceof Array && res.length > 0) {
res.map( booksFromSearch => {
booksOnShelves.find( book => {
if( booksFromSearch.id === book.id) {
booksFromSearch.shelf = book.shelf
return booksFromSearch;
} else {
booksFromSearch.shelf = 'none'
}
})
})

Resources