input text field for a particular item in array while mapping?

Viewed 612

Im trying to map over an array and trying to update a particular item based on a button click.

<div className="read-data">
    {readData.length >= 1 ? readData.map((res)=>(
     <div>
     <h2>{res.data.message}</h2>
     <button onClick={()=>{
          (setUpdateBox(true))
       }}>
       Update
     </button>
     {updateBox ? (
        <div>
          <form>
            I<input type="text" name="textarea" placeholder="Update message"/>
          </form>
        </div>
     ):null}
      <button onClick={()=>{
        deleteMessage(res)}}> Delete</button>
      </div>
    )):null}
  </div>

it opens text area for every item in array. but i want input text box for the only item whos corresponding button is clicked. plz help how to do it.

2 Answers

You could do something like this. Not sure if you want to use state or props or both etc.

Have a updateBox property on every object in the array, and toggle it between true and false.

You should probably add another function to update the message property as the user types in the input box?

function App(){

  function setUpdateBox(id){
    const copyState = [...readData];
    const thisItem = copyState.find(x => x.id == id);
    thisItem.updateBox = !thisItem.updateBox;
    setData(copyState);
  }
  
  function deleteMessage(id){
    const copyState = [...readData].filter(x => x.id != id);
    setData(copyState);
  }
  
  const initData = [
    {id: 1, updateBox: true, data: {message: "hello"}},
    {id: 2, updateBox: true, data: {message: "world"}},
  ]
  
  const [readData, setData] = React.useState(initData);
  
  return (
  <div className="read-data">
    {readData.length >= 1 ? readData.map((res)=>(
     <div>
     <h2>{res.data.message}</h2>
     <button onClick={()=>setUpdateBox(res.id)}>
       Update
     </button>
     {res.updateBox ? (
        <div>
          <form>
            <input type="text" name="textarea" placeholder="Update message"/>
          </form>
        </div>
     ):null}
      <button onClick={()=>deleteMessage(res.id)}> Delete</button>
      </div>
    )):null}
  </div>
  );
}

  
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

you may find that solution useful for you.

  • you need to set a unique index or any identifier to every element.
  • on every update or delete operation, you can access the element by its index
  • reference the input to get the value by using ref

I assume that you pass an array of object as a props

const readData = [
  {
    data: {
      message: "new message 1"
    }
  },
  {
    data: {
      message: "new message 2"
    }
  },
  {
    data: {
      message: "new message 3"
    }
  },
  {
    data: {
      message: "new message 4"
    }
  }
];

ReadData Component

function ReadDataComponent({readData}) {
  let inputRef = React.useRef();

  const [updateBox, setUpdateBox] = React.useState(readData);

  const deleteMessage = (i) => {
    const updatedBox = [...updateBox];
    let filteredBox = updatedBox.filter((_, index) => i !== index);
    setUpdateBox(filteredBox);
  };

  const showUpdateBox = (i) => {
    const updatedBox = [...updateBox];
    updatedBox[i].isOpen = updatedBox[i].isOpen ? !updatedBox[i].isOpen : true;
    updatedBox[i].data.message = inputRef.current
      ? inputRef.current.value
      : updatedBox[i].data.message;
    setUpdateBox(updatedBox);
  };

  return (
    <div className="read-data">
      {readData.length >= 1
        ? updateBox.map((res, i) => (
            <div key={i}>
              <h2>{res.data.message}</h2>
              <button
                onClick={() => {
                  showUpdateBox(i);
                }}
              >
                Update
              </button>
              {res.isOpen ? (
                <div>
                  <form>
                    <input
                      ref={inputRef}
                      type="text"
                      name="textarea"
                      placeholder="Update message"
                    />
                  </form>
                </div>
              ) : null}
              <button
                onClick={() => {
                  deleteMessage(i);
                }}
              >
                {" "}
                Delete
              </button>
            </div>
          ))
        : null}
    </div>
  );
}

live demo

Related