Reputation: 79
I have the following code and I'm trying to simplify it using a map function, perhaps on the array: const columns = ['Title', 'Author', 'Rating']
export const BookshelfListRow = (props) => {
return (
<tr className="table-row" >
<td>
<input onChange={(e) => { props.Update(e.target.value) }} placeholder={props.book.Title} />
</td>
<td>
<input onChange={(e) => { props.Update(props.book.Title, e.target.value) }} placeholder={props.book.Author} />
</td>
<td>
<input onChange={(e) => { props.Update(props.book.Title, props.book.Author, e.target.value) }} placeholder={props.book.Rating} />
</td>
</tr>
)}
Please note this is simplified - in my actual code I have 30 columns (meaning 30 separate inputs instead of 3) hence why I'm looking for a way to simplify it as it is currently really long - so essentially what is happening above is the placeholder is iterating through the array [Title,Author,Rating]
, and simultaneously on each new line we are adding an item from the array (in the form of props.book[item]
) to the props.Update function. Any ideas how I could use a map function to carry this out?
Upvotes: 2
Views: 5119
Reputation: 20821
const columns = ['Title', 'Author', 'Rating']
const update = (val, column) => {
console.log(`${column}: ${val}`)
}
const BookshelfListRow = () => (<table><tbody><tr className="table-row">{
columns.map((column, i) => {
return (<td key={i}><input type="text" onChange = {e => update(e.target.value, column)} placeholder={column} /></td >)
})
}</tr></tbody></table>
)
ReactDOM.render(
<BookshelfListRow />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Upvotes: 0
Reputation: 31682
You can use map
to simplify it. The tricky bit will be the calling of Update
with different number of parameters, but that too can be achieved using another map
.
const columns = ['Title', 'Author', 'Rating'];
export const BookshelfListRow = (props) => {
return (
<tr className="table-row">
{
columns.map((column, i) => (
<td>
<input onChange={ e =>
props.Update(...[ // the parameters to Update consist of
...columns.slice(0, i).map(column => props.book[column]), // the column values from the start until the current column, map is used here to get the values for those columns
e.target.value // and the input value
])
}
placeholder={ props.book[column] } />
</td>
))
}
</tr>
)
}
Another approach:
The Update
function is a mess. It can be a lot simpler if it just takes the column that was changed and the value as there is no need for it to send all those props back to the server if only one was changed, like so (this uses computed property names):
const Update = (column, value) => // takes the column that was changed and the value
axios.put('http://localhost:4001/books/update', { [column]: value }); // update only that column
Then the rendering will be much simpler also, like so:
const columns = ['Title', 'Author', 'Rating'];
export const BookshelfListRow = (props) => {
return (
<tr className="table-row">
{
columns.map((column, i) => (
<td>
<input onChange={ e => props.Update(column, e.target.value) } placeholder={ props.book[column] } />
</td>
))
}
</tr>
)
}
Upvotes: 1
Reputation: 2849
Mapping is a very powerful tool in React. It is most useful when you are try to DRY out some repeated code. In your case you are trying to DRY out your td
's by mapping over the array columns
.
Your columns array will need a little more info to make mapping useful. For instance,
const columns = ['Title', 'Author', 'Rating']
columns.map(column => console.log(column)) // Title, Author, Rating
That's not very helpful for your td
because it needs the onChange
, and placeholder
and both require more information than just the strings 'Title', 'Author', and 'Rating'.
From what I can tell, your book
prop is an object that looks something like this:
book: {
Title: 'some string',
Author: 'some other string',
Rating: 'some number maybe'
}
You can map over that by using Object.keys
. But again, that only helps with the placeholder
not the onChange
.
The data that you have and the data you are trying to use for your inputs do not seem to have a common enough pattern to utilize map here.
Possible Solution
Modify your update function to not require so many parameters to keep the input field generic as possible that way you can map over your columns.
export const BookshelfListRow = (props) => {
// if you are using React without hooks, just replace this with
// normal state
const [state, setState] = useState({
title: '',
author: '',
rating: ''
})
const update = (e) => {
const input = e.currentTarget.value;
const attribute = e.currentTarget.name;
setState({...state, [attribute]: input})
}
const apiRequest = (e) => {
e.preventDefault();
// your state is now your request body
request(state)
}
const columns = ['Title', 'Author', 'Rating']
return (
<tr className="table-row" >
{columns.map(column => (
<td key={column}>
<input name={column.toLowerCase()} onChange={update} placeholder={column} />
</td>
))}
</tr>
)}
Upvotes: 0
Reputation: 571
If you're using the keys of props.book
, you can try something like this:
import React from "react";
const BookshelfListRow = props => {
const args = [];
return (
<tr className="table-row">
{Object.keys(props.book).map((key, idx) => {
if(idx > 0) {
args.unshift(key);
}
const argsCopy = [...args];
return (
<td>
<input
onChange={e => {
props.Update(...argsCopy, e.target.value);
}}
placeholder={props.book[key]}
/>
</td>
);
})}
</tr>
);
};
export default BookshelfListRow;
Otherwise, you can use an array like the one you suggested (const columns = ['Title', 'Author', 'Rating']
) and take each value and add it to a copy with each map loop.
Upvotes: 0