React TableCell is not getting filled inside of nested loop - javascript

I have the following code. I am also console logging and the console is outputting values for every single piece of content. For some reason my Table.Cell's do not get filled up at all. I believe it is because I have a double nested loop.
const CustomToggle = ({ element}) => (
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Movies</Table.HeaderCell>
</Table.Row>
{Object.keys(element).map(function(dataType) {
{if (dataType !== "lid" && dataType !== "datacenter")
{Object.keys(element[dataType]).map(function(data){
console.log(element[dataType][data][0])
return (<Table.Row>
<Table.Cell>{element[dataType][data][0]}</Table.Cell>
</Table.Row>
)
})
}
}
})}
</Table.Header>
</Table>
);
Any idea how I can do this with a double nested loop? The table cells seems to get filled when I just do one loop. I am also getting the following warnings in react:
Nested block is redundant
Expected to return a value in function

Try this.
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Movies</Table.HeaderCell>
</Table.Row>
{Object.keys(element).map(function(dataType) {
{if (dataType !== "lid" && dataType !== "datacenter")
return Object.keys(element[dataType]).map(function(data){
console.log(element[dataType][data][0])
return (<Table.Row>
<Table.Cell>{element[dataType][data][0]}</Table.Cell>
</Table.Row>
)
})
}else
return <></>
})}
</Table.Header>
</Table>

You don't need two { here. That might be considered as no return or the return value goes nowhere. Also, indentation helps in these situations. Just replace it with the following:
const CustomToggle = ({ element }) => (
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Movies</Table.HeaderCell>
</Table.Row>
{Object.keys(element).map(function (dataType) {
if (dataType !== "lid" && dataType !== "datacenter") {
Object.keys(element[dataType]).map(function (data) {
console.log(element[dataType][data][0]);
return (
<Table.Row>
<Table.Cell>{element[dataType][data][0]}</Table.Cell>
</Table.Row>
);
});
}
return null;
})}
</Table.Header>
</Table>
);

Related

Render from object values - React js

I have this response object from an api, and I want to loop it and render it as if it was a normal array, how can I render tshirt, jeans and furniture? I will not like to render the value of sneakers, Any suggestion?
const items = {
tshirt: "Model TS",
jeans: "ModelXW",
sneakers: "indcdsc54",
furniture: "Table31S"
};
{Object.keys(items).map=>{i =>
<Card>
{items[key]}
</Card>
}
}
Try this one implementation line:
{Object.entries(items).filter(v => v[0] !== 'sneakers').map((v, idx) => <Card key={idx}>v[1]</Card>)}
You can read properties of an object using dynamic key: objectName[keyName]:
{
Object.keys(items).map(key => <Card key={key}>{items[key]}</Card>)
}
and to filter out sneakers:
{Object.keys(items).filter(key => key !== 'sneakers').map((key) => (
<Card key={key}>{items[key]}</Card>
))}
Instead of multiple loops, add an if condition to your code:
Object.keys(items).map(key => {
if (key != 'sneakers') {
return(<Card>{items[key]}</Card>);
}
});
We can use destructuring and it is definitely more readable.
const { sneakers, ...rest } = items;
Object.keys(rest).map((item, id) => {
<Card key={id}>
{item}
</Card>
}
);

retun values in function of array.map in react

Using React JSX, I have an array levels which can contain arrays of one or more of the levels with a name, for example: one, two and three. In my render function I can call {renderLevels} which renders all levels separated by a comma.
This works:
const renderLevels = levels.map((item, index) => {
return (
<Fragment key={index}>
{(index ? ' & ' : '')} {item.name}
</Fragment>
)
}
);
In case all levels are present I want to render 'all levels', instead of the comma separated list. In all other cases I want the list. So I change my code.
This does not work:
const renderLevels = () => {
if (levels.length === 3) {
return (
'all levels'
)
}
levels.map((item, index) => {
return (
<Fragment key={index}>
{(index ? ' & ' : '')} {item.name}
</Fragment>
)
}
)
};
My const is now a function I then call with {renderLevels()}. The problem: the list of item names is no longer returned when there are fewer than 3 levels. My if-statement works and console.log(item.name) inside .map does show me the results in case there are fewer than 3 levels. Getting the return values however does not. What am I doing wrong?
You are missing return for the levels.map(). Currently, you are just returning for the function, inside the if block but you have not returned anything when that if is not executed.
const renderLevels = () => {
if (levels.length === 3) {
return (
'all levels'
)
}
return levels.map((item, index) => {
return (
<Fragment key={index}>
{(index ? ' & ' : '')} {item.name}
</Fragment>
)
}
)
};

How to handle a generated big form in React Js

I trying to generate a big form based on what I get from the server.
sometimes I generate 32 elements sometimes 57 or 4 I don't know.
I try to create a component for each type of element like select, text, number, textarea and so on.
each component passes the value to the parent component and setState the value to the parent.
imagine I have 20 inputs and custom select-option elements.
when I type something in one of the inputs characters show up after 2seconeds and there is a huge lag in my component.
I know because of the setState method my hole component (I mean my parent component or my single source of truth) re-renders and causes the problem.
in fact, I don't know other ways.
I try to use a "this.VARIABLE" and instead of setState, I update the "this.VARIABLE" and problem solved. but I need my state.
any help or solution?
my code (parent Component, source of truth ):
// ---> find my component based on the type that I get from server
findComponent ( item , index) {
if ( item.type === 'text' || item.type === 'number') {
return (<Text data={item} getUpdated={this.fetchingComponentData} />);
} else if ( item.type === 'longtext') {
return (<Textarea data={item} getUpdated={this.fetchingComponentData} />);
} else if ( item.type === 'select' ) {
return (<SelectOption data={item} getUpdated={this.fetchingComponentData} />);
} else if ( item.type === 'autocomplete') {
return (<AutoTag data={item} url={URL1} getUpdated={this.fetchingComponentData} />);
} else if ( item.type === 'checkbox_comment' ) {
return (<CheckboxComment data={item} getUpdated={this.fetchingComponentData} />);
} else if ( item.type === 'multiselect' ) {
return (<Multiselect data={item} getUpdated={this.fetchingComponentData} />);
} else {
return (<p>THERE IS NO TYPE OF => {item.type}</p>);
}
}
// ----> if i setState here ==> big lag
fetchingComponentData(OBJ) {
let index = null;
// let Answer = [...this.state.Answer];
index = Helper.find_item(this.Answer , OBJ , 'unique_key');
if ( index === -1 ) {
this.Answer.push(OBJ);
} else {
this.Answer[index].value = OBJ.value;
}
}
// ----> in my render method
render () {
return (
<React.Fragment>
<div className="row Technical section" data-info="Technical">
<div className="col-6">
{data.map( (item,index) => {
return (
<React.Fragment key={index}>
<div className="rowi">
{item.attributes.map( (item, index)=> {
return <React.Fragment key={index}>{this.findComponent(item, index)}</React.Fragment>;
})}
</div>
</React.Fragment>
)
})}
</div>
<div className="col-6"></div>
</div>
</React.Fragment>
);
}
Have you tried to make an object out of your components and pass it to setState at once?
const nextState = componentList.map(component => {
return {[component]: value};
});
this.setState({...nextState});
Edit: Okay i got another part you could do better.
You should build an array with you components in componentWillMount function instead of fetching all the data inside the render. Like you said, it's updating everytime any state changes, and all the components are also updating with the parent.
This is to be made in addition with what I suggested before, but it is of far more importance because of the impact on the ressource.

filter vs map reactjs and jsx

I'm working on a react project to learn react.
In a component's render method, when I use .map to iterate over values and return an array of components, everything works as expected.
<ol className="books-grid">
{
books && books.map((book, index) => {
if (book.shelf === shelf) {
return (
<Book key={book && book.id ? book.id : index} changeShelf={this.props.changeShelf} book={book} />
);
}
})}
</ol>
But when I use filter:
<ol className="books-grid">
{
books && books.filter((book, index) => {
if (book.shelf === shelf) {
return (
<Book key={book && book.id ? book.id : index} changeShelf={this.props.changeShelf} book={book} />
);
}
})}
</ol>
I get the error (which I've researched)
Uncaught (in promise) Error: Objects are not valid as a React child
I don't understand why filter is throwing this error vs map? Is there something unique to react and .map? Both return an array.
Array.filter does not allow you to transform the data into components. That is the job of Array.map.
You should instead filter first, then chain the map call afterward:
{
books && books
.filter(book => book.shelf === shelf)
.map((book, index) => {
return (
<Book
key={book && book.id ? book.id : index}
changeShelf={this.props.changeShelf}
book={book} />
);
})
}
If you want to avoid a second pass over your list of books, you can return null as well, though this is "less good" because you're forcing React to render null when it doesn't need to do any work at all:
{
books && books
.map((book, index) => {
if (book.shelf !== shelf) {
return null;
}
return (
<Book
key={book && book.id ? book.id : index}
changeShelf={this.props.changeShelf}
book={book} />
);
})
}
There is nothing unique to React and map() or filter().
In the first example when using map() you are returning an array of React components which are rendered in the DOM. You are transforming (mapping) each plain JavaScript object in the array into a React component. As a matter of fact, you are also going to return some undefined elements in the resulting array, if the condition book.shelf === shelf is falsy. Your array may look like [<Book />, <Book />, undefined, <Book />, undefined]. That's not such a big deal, since React won't render falsy values (null or undefined elements will just be skipped).
The second example won't return the same result (an array of React components), but an array of plain JavaScript objects (of type book). This is because no matter what are you returning from the filter function, it's going to be cast to a Boolean value - true or false and that value is going to decide if the current element is going to be filtered or not. The result of your .filter() function is going to be something like this (imagine shelf === 'Science'):
Original array: [{ shelf: "Science" }, { shelf: "Thrillers" }, { shelf: "Informatics" }]
Filtered array: [{ shelf: "Science" }]
As you can see, the items in the array won't be React components (<Book />) and React won't be able to render them in the DOM, thus the error it throws.
If you only want to use one pass over the array you can use reduce:
books && books
.reduce(
(all,book, index) => {
if (book.shelf !== shelf) {
return all;
}
return all.concat(
<Book
key={book && book.id ? book.id : index}
changeShelf={this.props.changeShelf}
book={book} />
);
}
,[]
)
However; I think using filter and map makes for code that's easier to read.
The answer to me seems good but I needed to use includes() in order to work, also maybe a good idea to use toLowerCase():
{
books && books
.filter(book => book.shelf.toLowerCase().includes(shelf.toLowerCase()))
.map((book, index) => {
return (
<Book
key={book && book.id ? book.id : index}
changeShelf={this.props.changeShelf}
book={book} />
);
})
}
I also facing the same error. then, I also wrap my map method on filter method which helps me to solve the error and accept it as react valid child.
<ul className="menu">
{navigationLinks?.filter((eachNavigation) => {
if (eachNavigation.select.length < 1) {
return eachNavigation;
}
}).map((eachNavigation, index) => {
return (
<li key={index} className="menu__list menu__list--noSelect">
<a href={eachNavigation.link}>{eachNavigation.title}</a>
</li>
)
})
}
</ul>
<ul className="menu">
{navigationLinks?.filter((eachNavigation) => {
if (eachNavigation.select.length < 1) {
return eachNavigation;
}
}).map((eachNavigation, index) => {
return (
<li key={index} className="menu__list menu__list--noSelect">
<a href={eachNavigation.link}>{eachNavigation.title}</a>
</li>
)
})
}
</ul>

How conditionally render mapped object in mapped array

I have two different pieces of data coming into my component, sometimes an array of objects is passed in, and sometimes just an object. My goal is to loop through each object and spit out some JSX. Here is my code:
(Array.isArray(tableData))
?
(tableData.map(obj => {
(Object.keys(obj).map(key => {
return (
<tr>
<td>{key}</td>
<td>{obj[key]}</td>
</tr>
);
}))
}))
:
(Object.keys(tableData).map(key => {
return (
<tr key={key}>
<td>{key}</td>
<td>{tableData[key]}</td>
</tr>
);
}))
You can see im checking to see if the data coming in is an array, and if not loop through just a regular object. That part works fine, but if the data is an array, nothing gets displayed. What is wrong with my code that react doesnt render anything or throw any error messages?
Because you forgot to use return in this line:
(Object.keys(obj).map, try this:
Array.isArray(tableData))
?
tableData.map(obj => {
return Object.keys(obj).map(key => {
return (
<tr>
<td>{key}</td>
<td>{obj[key]}</td>
</tr>
);
})
})
:
Object.keys(tableData).map(key => {
return (
<tr key={key}>
<td>{key}</td>
<td>{tableData[key]}</td>
</tr>
);
})
Assign the unique key to element otherwise you will get a warning.
Mayank's answer solves the problem, but it's a bit verbose. Recall that if you want to return the result of a single expression (e.g. the result of a function call or a JSX element) from an arrow function, you can omit both the curly braces and return:
Array.isArray(tableData)
? tableData.map(obj =>
Object.keys(obj).map(key => (
<tr>
<td>{key}</td>
<td>{obj[key]}</td>
</tr>
)
))
: Object.keys(tableData).map(key => (
<tr key={key}>
<td>{key}</td>
<td>{tableData[key]}</td>
</tr>
))
I've used parentheses above just for clarity.
However, you're repeating the same code here twice, so for simplicity and readability I suggest extracting it into a function of its own:
const tableRows = obj =>
Object.keys(obj).map(key => (
<tr>
<td>{key}</td>
<td>{obj[key]}</td>
</tr>
)
);
// ...
Array.isArray(tableData) ? tableData.map(tableRows) : tableRows(tableData)

Categories

Resources