React模态框始终获取map函数的最后一个元素。

huangapple go评论75阅读模式
英文:

React modal always take the last element of map function

问题

我目前正在创建一个React待办事项应用程序。基本上,我有两个组件TodoList和TodoItem。

TodoList组件将接收一个包含标题属性的对象数组,并通过TodoItem组件对其进行映射。

在我的TodoItem组件中,用户可以选择编辑或删除项目。如果用户选择编辑,将显示一个模态框,其中包含使用文本区域的现有标题。然而,我在实现此功能时遇到了问题,因为模态框总是显示数组的最后一个元素。

TodoList组件

import React, { Component } from 'react'
import TodoItem from './TodoItem'
import { connect } from 'react-redux'
import { clear_todo } from '../store/actions/todoActions'

class Todolist extends Component {

  clearList = (e) => {
    e.preventDefault()
    this.props.clearList(clear_todo());
  }

  handleChange = (index, title) => {
    this.setState({
      [index]: title
    })
  }

  render() {
    const { items, editItem } = this.props.todo
    return (
      <ul className="list-group my-5">
        <h3 className="text-capitalize text-center">
          Todo List
        </h3>
        {
          items.map((item, index) => {
            const editedTitle = item.title

            return (<TodoItem key={item.id} title={item.title}
              id={item.id}
              editedTitle={editedTitle}
              onChange={this.handleChange}
              editItem={editItem} />)
          })
        }

        <button className="btn btn-danger btn-block text-capitalize mt-5" onClick={this.clearList}> Clear List </button>
      </ul>
    )
  }
}

const mapStateToProps = state => {
  return {
    todo: state.todo
  }
}

const mapDispatchToProps = dispatch => {
  return {
    clearList: (clear_todo) => { dispatch(clear_todo) }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Todolist)

TodoItem组件

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { delete_todo, edit_todo, toggle_edit } from '../store/actions/todoActions'
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Form, FormGroup, Input } from 'reactstrap';
import TodoEditItem from './TodoEditItem';

class Todoitem extends Component {

  handleEdit = (id, title) => {
    this.props.editTodo(edit_todo(id, title))
  }

  toggleEdit = (editItem, title) => {
    this.props.toggleEdit(toggle_edit(!editItem))
  }

  handleDelete = (id) => {
    this.props.deleteTodo(delete_todo(id))
  }

  componentDidMount() {
    this.setState({
      [this.props.id]: this.props.editedTitle
    })
  }

  render() {
    let { id, title, editItem, editedTitle, index } = this.props

    return (
      <div>
        <li className="list-group-item text-capitlize d-flex justify-content-between my-2">
          <h6>{title}</h6>
          <div className="todo-icon">
            <span className="mx-2 text-success" onClick={this.toggleEdit.bind(this, editItem)} >
              <i className="fas fa-pen"></i>
            </span>
            <span className="mx-2 text-danger" onClick={this.handleDelete.bind(this, id)}>
              <i className="fas fa-trash"></i>
            </span>
          </div>

          <Modal isOpen={editItem}>
            <ModalHeader>Edit Todo Item</ModalHeader>
            <ModalBody>
              <Form>
                <FormGroup row>
                  <Input type="textarea" name="text" value={this.state ? this.state[id] : ""} onChange={this.props.onChange} />
                </FormGroup>
              </Form>
            </ModalBody>
            <ModalFooter>
              <Button color="primary" onClick={this.handleEdit.bind(this, id, editedTitle)}>Save</Button>{' '}
              <Button color="secondary" onClick={this.toggleEdit.bind(this, editItem)}> Cancel</Button>
            </ModalFooter>
          </Modal>
        </li>
      </div>
    )
  }
}

const mapDispatchToProps = dispatch => {
  return {
    deleteTodo: (delete_todo) => { dispatch(delete_todo) },
    editTodo: (edit_todo) => { dispatch(edit_todo) },
    toggleEdit: (toggle_edit) => { dispatch(toggle_edit) },
  }
}

export default connect(null, mapDispatchToProps)(Todoitem)

如你从TodoItem的图像中所见,我正在尝试编辑"Take out the garbage",但我的文本区域已经预填充为数组的最后一个元素。

英文:

I am currently creating a React todo application. So basically I have two component TodoList and TodoItem

TodoList component will receive an array of objects consisting title as the property and map through this with TodoItem component

In my TodoItem component, the user can choose to edit or delete the item. If the user chose to edit, a modal will show up with the existing title using a textarea. However, I have trouble implementing this function as the modal will always show up with the last element of the array.

TodoList Component

import React, { Component } from &#39;react&#39;
import TodoItem from &#39;./TodoItem&#39;
import { connect } from &#39;react-redux&#39;
import { clear_todo } from &#39;../store/actions/todoActions&#39;
class Todolist extends Component {
clearList = (e) =&gt; {
e.preventDefault()
this.props.clearList(clear_todo());
}
handleChange = (index, title) =&gt; {
this.setState({
[index]: title
})
}
render() {
const { items, editItem } = this.props.todo
return (
&lt;ul className=&quot;list-group my-5&quot;&gt;
&lt;h3 className=&quot;text-capitalize text-center&quot;&gt;
Todo List
&lt;/h3&gt;
{
items.map((item, index) =&gt; {
const editedTitle = item.title
return (&lt;TodoItem key={item.id} title={item.title}
id={item.id}
editedTitle={editedTitle}
onChange={this.handleChange}
editItem={editItem} /&gt;)
})
}
&lt;button className=&quot;btn btn-danger btn-block text-capitalize mt-5&quot; onClick={this.clearList}&gt; Clear List &lt;/button&gt;
&lt;/ul&gt;
)
}
}
const mapStateToProps = state =&gt; {
return {
todo: state.todo
}
}
const mapDispatchToProps = dispatch =&gt; {
return {
clearList: (clear_todo) =&gt; { dispatch(clear_todo) }
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Todolist)

TodoItem Component

import React, { Component } from &#39;react&#39;
import { connect } from &#39;react-redux&#39;
import { delete_todo, edit_todo, toggle_edit } from &#39;../store/actions/todoActions&#39;
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Form, FormGroup, Input } from &#39;reactstrap&#39;;
import TodoEditItem from &#39;./TodoEditItem&#39;
class Todoitem extends Component {
// constructor(props) {
//   super(props)
//   this.state = {
//     [props.id]: props.title
//   }
// }
handleEdit = (id, title) =&gt; {
this.props.editTodo(edit_todo(id, title))
}
toggleEdit = (editItem, title) =&gt; {
this.props.toggleEdit(toggle_edit(!editItem))
// this.initializeTitle(title)
}
handleDelete = (id) =&gt; {
this.props.deleteTodo(delete_todo(id))
}
// onChange = (e, id) =&gt; {
//   this.setState({
//     [id]: e.target.value
//   })
// }
componentDidMount() {
// console.log(this.props)
// this.initializeTitle(this.props.title)
this.setState({
[this.props.id]: this.props.editedTitle
})
console.log(this.state)
}
render() {
// console.log(this.state)
let { id, title, editItem, editedTitle, index } = this.props
console.log(id)
// console.log(index)
// let { item } = this.state
return (
&lt;div&gt;
&lt;li className=&quot;list-group-item text-capitlize d-flex justify-content-between my-2&quot;&gt;
&lt;h6&gt;{title}&lt;/h6&gt;
&lt;div className=&quot;todo-icon&quot;&gt;
&lt;span className=&quot;mx-2 text-success&quot; onClick={this.toggleEdit.bind(this, editItem)} &gt;
&lt;i className=&quot;fas fa-pen&quot;&gt;&lt;/i&gt;
&lt;/span&gt;
&lt;span className=&quot;mx-2 text-danger&quot; onClick={this.handleDelete.bind(this, id)}&gt;
&lt;i className=&quot;fas fa-trash&quot;&gt;&lt;/i&gt;
&lt;/span&gt;
&lt;/div&gt;
&lt;Modal isOpen={editItem}&gt;
&lt;ModalHeader&gt;Edit Todo Item&lt;/ModalHeader&gt;
&lt;ModalBody&gt;
&lt;Form&gt;
&lt;FormGroup row&gt;
&lt;Input type=&quot;textarea&quot; name=&quot;text&quot; value={this.state ? this.state[id] : &quot;&quot;} onChange={this.props.onChange} /&gt;
&lt;/FormGroup&gt;
&lt;/Form&gt;
&lt;/ModalBody&gt;
&lt;ModalFooter&gt;
&lt;Button color=&quot;primary&quot; onClick={this.handleEdit.bind(this, id, editedTitle)}&gt;Save&lt;/Button&gt;{&#39; &#39;}
&lt;Button color=&quot;secondary&quot; onClick={this.toggleEdit.bind(this, editItem)}&gt; Cancel&lt;/Button&gt;
&lt;/ModalFooter&gt;
&lt;/Modal&gt;
&lt;/li&gt;
{/* {editItem ? &lt;TodoEditItem title={title} editItem={editItem} /&gt; : &#39;&#39;} */}
&lt;/div&gt;
)
}
}
const mapDispatchToProps = dispatch =&gt; {
return {
deleteTodo: (delete_todo) =&gt; { dispatch(delete_todo) },
editTodo: (edit_todo) =&gt; { dispatch(edit_todo) },
toggleEdit: (toggle_edit) =&gt; { dispatch(toggle_edit) },
}
}
export default connect(null, mapDispatchToProps)(Todoitem)

Sample Image

Image of TodoItem

Image of Modal

As you can see from the image of TodoItem, I am trying to edit "Take out the garbage" but my textarea has been prepopulated as the last element.

答案1

得分: 5

你正在使用相同的变量来确定所有TodoItem的打开状态。结果是看起来它只取数组中的最后一个值,但实际上每个模态框都在同时打开,只有最后一个可见。

// 这里的editItem用于确定两个模态框的打开状态
const { items, editItem } = this.props.todo 
...
{
  items.map((item, index) => {
    const editedTitle = item.title
    return (
      <TodoItem 
        key={item.id} 
        title={item.title}
        id={item.id}
        editedTitle={editedTitle}
        onChange={this.handleChange}
        editItem={editItem}            // 相同的值
      />
    )
  })
}

...

let { editItem } = this.props
<Modal isOpen={editItem}>             // 所有模态框将同时打开和关闭

相反,使用不同的标志,或者像这样在每个子项中管理打开状态:

<span className="mx-2 text-success" onClick={() => this.setState({open: true})} >
  <i className="fas fa-pen"></i>
</span>

...

<Modal isOpen={this.state.open}>
英文:

You're using the same variable to determine all of the TodoItem's open state. The result is it appears that it is only taking the last value from the array, but really each modal is opening at once and the last one is the only visible modal.

// editItem here is used to determine the open state of both modals
const { items, editItem } = this.props.todo 
...
{
  items.map((item, index) =&gt; {
    const editedTitle = item.title
    return (
      &lt;TodoItem 
        key={item.id} 
        title={item.title}
        id={item.id}
        editedTitle={editedTitle}
        onChange={this.handleChange}
        editItem={editItem}            // Same value
      /&gt;
    )
  })
}

...

let { editItem } = this.props
&lt;Modal isOpen={editItem}&gt;             // All will open and close at the same time

Instead, use a different flag, or just manage the open state in each child like this:

&lt;span className=&quot;mx-2 text-success&quot; onClick={() =&gt; this.setState({open: true})} &gt;
  &lt;i className=&quot;fas fa-pen&quot;&gt;&lt;/i&gt;
&lt;/span&gt;

...

&lt;Modal isOpen={this.state.open}&gt;

huangapple
  • 本文由 发表于 2020年1月4日 00:27:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/59581998.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定