Gazdini9991
Gazdini9991

Reputation: 79

React JS map function

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

Answers (4)

ksav
ksav

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

ibrahim mahrir
ibrahim mahrir

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

Bens Steves
Bens Steves

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

Micah Wierenga
Micah Wierenga

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

Related Questions