edmamerto
edmamerto

Reputation: 8165

ReactJS selecting an element uniquely from a map

I am doing a todo app to practice React. I hit a blocker and now I'm trying to figure out how to uniquely edit a card.

Currently when I click on edit, all my cards are set to isEditing == true. I've tried adding a key and index, but doesn't seem to uniquely identify the selected card.

As seen in my gif:

enter image description here

Obviously the expected outcome is that it should only set isEditing == true to the selected card.

See Code below.

For more context: there is stateful component that passes the props to this component, I'm using react-bootstrap (hence Panel, Button), and I removed some code for brevity (construct and whatnot).

    edit() {
        this.setState({
            isEditing: true
        })
    }

    renderEditDoneButtons() {
        return (
            <div>
                <Button onClick={this.edit}>edit</Button>
            </div>
        )
    }

    renderNote(note) {
        return (
            <p> {note} </p>
        )
    }

    renderCard(note, i) {
        return (
            <Panel key={i}
                   index={i}>
                {
                    this.state.isEditing ? 
                    this.renderForm() : 
                    this.renderNote(note.note)
                }
            </Panel>
        )
    }

    render() {
        return (
            <div>
                {this.props.notes.map(this.renderCard)}
            </div>
        )
    }  

Upvotes: 2

Views: 178

Answers (3)

Kody R.
Kody R.

Reputation: 2460

All three are changing based on your single isEditing state, which is why you're seeing all three being shown when you click any of the "Edit" buttons. Instead of a single isEditing key in state, use an array to maintain all three states like so:

constructor(props) {

  super(props);

  // Sets a true/false editing state for all three panels
  this.state = {
    editingPanels: Array(3).fill(false)
  }
}

edit(i) {

    // Switches editing state to false/true for given i
    const editingPanels = this.state.editingPanels.slice();
    editingPanels[i] = !editingPanels[i];

    this.setState({
        editingPanels: editingPanels
    })
}

renderEditDoneButtons(i) {
    return (
        <div>
            <Button onClick={()=>this.state.edit(i)}>edit</Button>
        </div>
    )
}

renderNote(note) {
    return (
        <p> {note} </p>
    )
}

renderCard(note, i) {
    return (
        <Panel key={i}
               index={i}>
            {
                this.state.editingPanels[i] ? 
                this.renderForm() : 
                this.renderNote(note.note)
            }
        </Panel>
    )
}

render() {
    return (
        <div>
            {this.props.notes.map(this.renderCard)}
        </div>
    )
} 

Upvotes: 4

Sahil Arora
Sahil Arora

Reputation: 491

You need to have state variable isEditing for each particular card. If there are 3 cards, you need to have 3 variables.

Edit 1 :- Example is already shared by Kody R.

One Thing i noticed is instead of hard-coding array size to 3,we could assign array size by number of notes recieved in props.

this.state = {
    editingPanels: Array(3).fill(false)
  }

To

this.state = {
    editingPanels: Array(this.props.notes.length).fill(false)
  }

Hope this helps,

Cheers !!

Upvotes: 0

Harikrishnan
Harikrishnan

Reputation: 1097

You can use a separate component for each todo list item and use it inside the map method.The following example gives an idea on how to implement this.I am using another example as you have not provided the full code.

class EditText extends React.Component {
  constructor(props) {
    super(props)
    this.state = {value:props.data,newValue:'hi'}
    this.editValue = this.editValue.bind(this)
  }
  editValue() {
    this.setState({value:this.state.newValue})
  }
  render() {
    return(
      <div>
      {this.state.value}
      <button onClick={this.editValue}>Change text to Hi</button>
      </div>
    )
  }
}
class App extends React.Component {
  constructor() {
    super()
    this.state = {tempDate : ['hello','how']}
  }
  render() {
    return (
      <div className="App">
      {this.state.tempDate.map(data=>(<EditText data={data}/>))}
      </div>
    );
  }
}

Upvotes: 1

Related Questions