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?
Related
I'm currently using child components which returns JSX.
//PARENT COMPONENT
import ApprovalTableElement from './E_Approval_Element.js';
//JSX of E_Approval_Element.js
const [approvalElement, setApprovalElement] = useState([ApprovalTableElement]);
//Add one more approval column
const addApprovalSpace = () => {
setApprovalElement([...approvalElement, ApprovalTableElement]);
};
return (
<div className={styles['EApprovalWriteDiv']}>
<div className={styles['authDiv']}>
{approvalElement.map((element, index) => (
<button>DELETE ONE APPROVAL SECTION</button>
<ApprovalTableElement index={index} />
))}
</div>
</div>
);
};
export default E_Approval_Write;
//CHILD COMPONENT
function ApprovalTableElement() {
return (
<>
<table className={styles['approvalTable']}>
<tbody className={styles['approval']}>
<tr className={styles['name']}>
<th>
<select style={{ marginLeft: 10 }}>
<option>선택</option>
<option>결재자</option>
<option>합의자</option>
</select>
</th>
</tr>
<tr className={styles['signature']}>
<td>
<div>SIGN</div>
</td>
</tr>
<tr className={styles['name']} onClick={memberModalTrigger}>
<td>
<Typography variant='button' display='block'>
</Typography>
</td>
</tr>
</tbody>
</table>
</>
);
}
export default ApprovalTableElement;
with this code, what I'm trying to do is using
{approvalElement.map((element, index) => (
<button>DELETE ONE APPROVAL SECTION</button>
<ApprovalTableElement index={index} />
))}
this button, deleting selected ApprovalTableElement.
right now, I have this UI. When I click + button, I keeps adding component. But when I click remove button, the table attached to the button should disappear. BUT not the other ones.
All I can know is the index of the Component, so I am really confusing on how to delete the targeted component using filter().
OR, should I add button tag inside the CHILD COMPONENT not the PARENT COMPONENT?
However, If I can make what I'm trying to do with this code, please tell me how to set up the code properly to make things possible. Thank you!
Just pick those which id is different from the one you are deleting
const removeApprovalSpace = (id) => {
setApprovalElement(items => items.filter(item => item.id !== id));
};
//usage
<button onClick={() => removeApprovalSpace(id)}>Remove</button>
If you don't have id's you can use index
const removeApprovalSpace = (index) => {
setApprovalElement(items => items.filter((item, i) => i !== index));
};
//usage
<button onClick={() => removeApprovalSpace(index)}>Remove</button>
I am trying to call an async function using .map() in REACT to populate table data cells. The async function is calling the backend of my application using data from the array that is being iterated through.
The function that is being called returns a number when called properly.
This is my first time posting a question so any help is great! If I need to add more information I will.
Thank you.
{tableInfo.map(info => (
<tr>
<td key={info.name}>{info.name}</td>
<td key={info.price}>{info.price}</td>
<td key={info.amount}>{info.amount}</td>
<td>
{async () => await fetchCurrentPrice.getPrice(info.name)}
</td>
</tr> ))}
async functions always return promises. You can't fetch data asynchronously during rendering.
You need a useEffect hook to run the function which gathers the data, and then a useState hook to store it, along with logic to show a loading state while you wait for it.
const MyRow = ({ info }) => {
const [currentPrice, setCurrentPrice] = useState(null);
useEffect(() => {
fetchCurrentPrice.getPrice(info.name).then(setCurrentPrice);
}, [info]);
return <tr>
<td key={info.name}>{info.name}</td>
<td key={info.price}>{info.price}</td>
<td key={info.amount}>{info.amount}</td>
<td>{currentPrice ?? <Loading />}</td>
</tr>
}
with
{tableInfo.map(info => <MyRow key={info.name} info={info} />)}
Use a separate component for the tr tag and call it within it.
const Component = (props) => {
const {info} = props
useEffect(() => {
// call your api and set it to state
}, [])
return <tr>
<td key={info.name}>{info.name}</td>
<td key={info.price}>{info.price}</td>
<td key={info.amount}>{info.amount}</td>
<td>
{async () => await fetchCurrentPrice.getPrice(info.name)}
</td>
</tr>
}
{tableInfo.map(info => <Component info={info} />)}
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
I have working reactJS code that loads data from an excel sheet onto a table component. If the table is empty, it works just fine.
However, if I try to load another excel file, it only updates part of the table and not the stuff already occupied.
This is how I load a table:
table = () => {
return (<Table striped bordered hover size="sm" ref={this.configUploadRef}>
<tbody onChange={this.edit}>
{this.state.data.map(row => {
return (
<tr ref={c => this.tdRef[this.state.data.indexOf(row)] = c}>
{row.map(cell => {
return (
<td>
<EditableLabel text={cell}/>
</td>)
}
)}
</tr>)
})}
</tbody>
</Table>)
}
And this is how I upload a new excel sheet.
configFile = async configFile => {
this.tdRef = [];
await readXlsxFile(configFile).then((rows) => {
this.setState({data: rows})
}).then(
console.log(this.table())
)
}
Is there any way I can delete all of the data in the table and then populate it with the new stuff?
Figured it out.
Add key props, that's it.
<tr ref={c => this.tdRef[i] = c} key={randomstring.generate(12)}>
and
<td key={randomstring.generate(12)}>
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)