How conditionally render mapped object in mapped array - javascript

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)

Related

dynamic table data is not getting rendered in react

I am trying to render a table on expanding the icon
expandable={{
expandedRowRender: () => {
return (
<table>
<tbody>
{props.data.map(obj => {
Object.entries(obj).forEach(([key, value]) => {
return (
<tr>
<td>{key}</td>
<td>{value}</td>
</tr>
)
})
})}
</tbody>
</table>
)
},
but this isnt rendering anything, The table data is dynamic keys and values from the object. can someone tell me what im doing wrong here?

Cannot read property 'map' of undefined

I have an error of Cannot read property of map, I'm trying to display a table of content on my page by getting the data from the database using restful API and store it into an array.
first time I open the site everything works fine and it displays the table with no problem but when I refresh it gives the error.
This is my function for getting the data and display it in a table using map:
const ListTodos = ()=>{
const [todos,setToods]=useState([]);
const getTodos = async ()=>
{
try{
const res = await fetch("http://localhost:3080/get")
console.log(res)
const jsondata = await res.json();
setToods(jsondata);
}catch(err){
console.error(err.message);
}
}
useEffect(() => {
getTodos();
}, []);
arr = todos[0];
return (
<Fragment>
<table class="table mt-5 text-center">
<thead>
<tr>
<th>ID_Event</th>
<th>Name_Event</th>
<th>Date</th>
<th>Address</th>
<th>Duration</th>
<th>Description</th>
<th>Delete</th>
<th></th>
</tr>
</thead>
<tbody>
{arr.map(arr => (
<tr>
<td>{arr.ID_Event}</td>
<td>{arr.Name_Event}</td>
<td>{arr.Date}</td>
<td>{arr.Address}</td>
<td>{arr.Duration}</td>
<td>{arr.description}</td>
<td>
<button className="btn btn-danger" onClick={() => deleteevnet(arr.ID_Event)}>Delete</button>
</td>
</tr>
))}
</tbody>
</table>
</Fragment>
);
}
By writing arr = todos[0] on top level you are creating a global variable which is undefined until the data is fetched (first element of empty array).
You should declare it ideally as const so it is local to the functional component, and handle the case when it's undefined with something like:
{arr && arr.map(arr => (
<tr>
<td>{arr.ID_Event}</td>
<td>{arr.Name_Event}</td>
<td>{arr.Date}</td>
<td>{arr.Address}</td>
<td>{arr.Duration}</td>
<td>{arr.description}</td>
<td>
<button className="btn btn-danger" onClick={() => deleteevnet(arr.ID_Event)}>Delete</button>
</td>
</tr>
))}
Add a conditional so it runs when your arr is not undefined:
{arr !== "undefined" && arr.map(arr => (
React evaluates our return statement, when it hits the arr.map(...)
line its actually running undefined.map(...) which is obviously an
error in JavaScript.
Read more here
Use the following programming construct to ensure the arr has value inside it:
{arr && arr.map(arr => (
// ...
))}
The useEffect hook works asynchronously so on the first run the arr would be undefined:
arr = todos[0]; // [][0] === undefined

Parsing error: Unexpected token when adding a conditional in react

Hi I'm a beginner in react, I was getting an error for scenarios when no props get passed to my component, it said something like "you can't do a map of undefined" so I added this if statement. But now I get a parsing error on line 4. Not sure' what's wrong with my code:
import React from 'react';
export const Record = props => (
if (props) {
<div className='card-container'>
{props.rows.drivingInstructions.map((val, ind) => {
return (
<tr>
<td>{ind + 1}</td>
<td>Smith</td>
<td>50</td>
</tr>
)
})}
</div>
}
{ console.log(props.rows.drivingInstructions) }
);
if can't exist in an expression context. For similar reasons, you can't do:
const someVar = if (true) 10 else null;
The syntax is not permitted. To do something like this, you need the conditional operator:
export const Record = props => (
!props.rows || !props.rows.drivingInstructions ? null : (
<div className='card-container'>
{props.rows.drivingInstructions.map((val, ind) => {
...
Or, if you're OK with the .card-container existing even if it doesn't have any rows, use optional chaining instead:
export const Record = props => (
<div className='card-container'>
{props.rows.drivingInstructions?.map((val, ind) => {
...
I would rewrite those code as below.
import React from 'react';
export const Record = (props) => {
if (!!props?.rows?.drivingInstructions?.length) {
return (<div className='card-container'>
{props.rows.drivingInstructions.map((val, ind) =>(
<tr>
<td>{ind + 1}</td>
<td>Smith</td>
<td>50</td>
</tr>
)
)}
</div>);
}
return <div>No Driving Instructions</div>;
}
};
This !!props?.rows?.drivingInstructions?.length is an optional chaining and it checks that those properties are not nullish. Lastly, it ensures the length is also more than 0 by converting to boolean using !!.
import React from 'react';
export const Record = props => (
props?.rows?.drivingInstructions?.length ?
(<div className='card-container'>
{props.rows.drivingInstructions.map((val, ind) =>
<tr>
<td>{ind + 1}</td>
<td>Smith</td>
<td>50</td>
</tr>
)}
</div>) : null;
);

React TableCell is not getting filled inside of nested loop

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

Filter items with .filter .map .some .indexOf etc

I'm building a recipe generator in react.js and es6.
Im trying to filter the recipes in a good way and right now I'm just checking if my recipes includes any of the choosen ingredients.
I want it to first check if my Baseingredient is in the recipe-ingredient array and then filter the rest after that is done. So the baseingredient MUST be included.
The baseingredient and the choosen ingredients are all in the same ingredientArray(cartIds), so the "cartIds" also includes the Id of my currentBaseingredient.
This is what my code looks like today.
const recipes = () => {
return { recipes: Store.getRecipes(), cart: Store.getCart(), baseingredient: Store.getCurrentBaseIngredient() }
}
const Recipes = ( props ) => {
var cartIds = props.cart.map(item => item.ingredientId); // Ex: [11, 23, 1]
// TODO: this ingredient-id must be in the recipes ingredient array!!
var baseIngredient = props.baseingredient.ingredientId;
console.log('baseingredient id:',baseIngredient); // Ex: 1
var recipes = props.recipes
.filter(recipe => ( // Run filter function on all recipes
recipe.ingredients.some(ingredient => ( // Check if reciepe contains any of the chosen ingredients
cartIds.indexOf(ingredient.ingredientId) >= 0) // Ingredient check
)
)) // Now we have a list of reciepes which contain some of the chosen ingredients
.map( ( recipes, i ) => {
return (
<RecipesItem
key={i}
recipe={recipes}/>
)
} );
return (
<div>
<table className="table table-hover">
<thead>
<tr>
<th>Recipe</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{recipes}
</tbody>
</table>
</div>
);
}
Is something like below what you need? Also you were missing the return keyword in your filter before
var recipes = props.recipes
.filter(recipe => ( // Run filter function on all recipes
return recipe.ingredients.includes(baseIngredient) && recipe.ingredients.some(ingredient => ( // Check if reciepe contains any of the chosen ingredients
cartIds.indexOf(ingredient.ingredientId) >= 0) // Ingredient check
)
)) // Now we have a list of reciepes which contain some of the chosen ingredients
.map( ( recipes, i ) => {
return (
<RecipesItem
key={i}
recipe={recipes}/>
)
} );

Categories

Resources