2017-11-26 6 views
2
class ToDo extends React.Component { 
    constructor() { 
    super() 
    this.state = { 
     todos: [], 
     inputValue: null 
    } 
    this.changeValue = this.changeValue.bind(this) 
    this.addTodo = this.addTodo.bind(this) 
    this.removeTodo = this.removeTodo.bind(this) 
    } 

    changeValue(e) { 
    this.setState({inputValue: e.target.value}) 
    } 

    addTodo(e) { 
    e.preventDefault() 

    const newTodoItem = { 
     id: Date.now(), 
     inputValue: this.state.inputValue 
    } 

    if(!this.state.inputValue.length) return 

    this.setState(state => ({ 
     todos: state.todos.concat(newTodoItem), 
     inputValue: '' 
    })) 
    } 

    removeTodo(e) { 
    const et = e.target 
    const todos = this.state.todos 

    this.setState({ 
     todos: todos.splice(todos.indexOf(et), 1) 
    }) 
    } 

    render() { 
    return(
     <div> 
     <form onSubmit={this.addTodo}> 
      <input 
      type='text' 
      name='todo' 
      placeholder='Enter a ToDo' 
      value={this.state.inputValue} 
      onChange={this.changeValue} 
      /> 
      <input type='submit' value='Add' /> 
      &nbsp;&nbsp;&nbsp; 
      <span>ToDos: {this.state.todos.length}</span> 
     </form> 
     <TodoList items={this.state.todos} itemClickEvent={this.removeTodo} /> 
     </div> 
    ) 
    } 
} 

class TodoList extends React.Component { 
    render() { 
    const items = this.props.items 
    return(
     <ul> 
     { 
      items.map(item => (
      <li key={item.id} onClick={this.props.itemClickEvent}>{item.inputValue}</li> 
     )) 
     } 
     </ul> 
    ) 
    } 
} 

ReactDOM.render(<ToDo />, document.querySelector('#app')) 

In dieser ToDo-Liste Probe, die ich gerade von der Probe in der Homepage reactjs.org imitiert, fügte ich ein Ereignis, das die gezielte todo Element entfernt, wenn geklickt. Das Problem ist, egal, welche der Aufgaben, auf die ich klicke, statt das Zielobjekt selbst zu entfernen, es entfernt alle Elemente mit Ausnahme des letzten Elements. Warum ist ein solcher Fehler passiert?onClick Ereignis nur verlässt das letzte Element des DOM-Array statt das Ziel der Beseitigung

+0

können Sie console.log todos.indexOf (et) auf removeTodo? –

+0

es protokolliert '-1' für jeden Artikel – juntarnate16

Antwort

1

Sie sollten ein paar Änderungen vornehmen, diese Arbeit richtig zu machen:

  • Verwenden filter statt splice, als filter gibt eine neue Array zu Modifizierung der vorhandenen Array gegenüber. Es empfiehlt sich, den vorhandenen Status nicht zu ändern.
  • Verwenden Sie statt todos vor setState die Variante setState, die den aktuellen Status als ersten Parameter an eine Funktion in setState übergibt.
  • Fügen Sie den li Elementen, die Sie in Ihrer removeTodo Funktion referenzieren können, einen eindeutigen Bezeichner hinzu. Im Moment wird todos.splice(todos.indexOf(et), 1); wird nichts sinnvolles zurückgeben, da et ist ein Element, und todos ist kein Array von Element s - es ist ein Array von Objekten, die zwei Schlüssel haben: id und inputValue. Stattdessen können Sie ein data-key-Attribut zu Ihrem li hinzufügen, das dem key-Attribut entspricht, und dann auf data-key in Ihrem removeTodo verweisen.

Wenn Sie es alle zusammen, das ist die Änderung, die Sie an die JSX machen von der TodoList Klasse zurückgegeben:

items.map(item => (
    <li key={item.id} data-key={item.id} onClick={this.props.itemClickEvent}>{item.inputValue}</li> 
)) 

Beachten Sie, dass wir data-key gleich item.id gesetzt.

Unsere modifizierte removeTodo wird wie folgt aussehen:

removeTodo(e) { 
    const et = e.target 
    const attr = et.getAttribute("data-key"); 

    this.setState(state => ({ 
     todos: state.todos.filter(item => item.id != attr) 
    })) 
} 

Ich habe Ihre JSFiddle here mit einem Arbeitsbeispiel modifiziert.

Edit:

Obwohl über die Lösung ganz gut funktioniert, gibt es eine alternative Lösung, die kürzer ist und vermeidet ein data-key Attribut auf den input s hinzufügen zu müssen:

Pass die item.id direkt an removeTodo durch diese Änderung:

items.map(item => (
    <li key={item.id} onClick={() => this.props.itemClickEvent(item.id)}>{item.inputValue}</li> 
)) 

Dann vereinfachen Ihre removeTodo b arbeiten y mit seinen id Parametern:

removeTodo(id) { 
    this.setState(state => ({ 
     todos: state.todos.filter(item => item.id != id) 
    })) 
} 

JSFiddle here mit der neuen Lösung.

+0

Vielen Dank. Ich habe nur eine Frage ... Was macht einen 'setState' mit Funktion, die ein Objekt zurückgibt, das sich vom regulären' setState' unterscheidet? – juntarnate16

+0

Die Funktion, die den neuen Zustand zurückgibt, wird formal als "Updater" bezeichnet und sollte immer dann verwendet werden, wenn Ihr neuer Zustand von Ihrem alten Zustand abhängt (in Ihrem Beispiel hängt Ihr neuer Zustand von den Todos Ihres alten Zustandes ab) Das ist der einzige Ort, an dem Sie garantiert ein aktuelles 'State'-Objekt haben. Theoretisch gesprochen, wenn Sie Ihren 'state' vor dem Aufruf von' setState() 'verwenden, könnte Ihr Zustand (bis zur Ausführung von setState()') veraltet sein (modifiziert durch eine andere Funktion). Aus der Dokumentation: "Wenn der nächste Status vom vorherigen Status abhängt, empfehlen wir die Verwendung des Updater-Funktionsformulars" –

+1

Notiert. Danke vielmals :) – juntarnate16

0

Hier gehen wir, versuchen Sie dies:

removeTodo(e) { 
    const et = e.target 
    const todos = this.state.todos 
    todos.splice(todos.indexOf(et), 1); 
    this.setState({ 
     todos 
    }) 
} 

splice() Rückkehr:

Ein Array die gelöschten Elemente enthält. Wenn nur ein Element entfernt wird, wird ein Array mit einem Element zurückgegeben. Wenn keine Elemente entfernt sind, wird ein leeres Array zurückgegeben.

Verwandte Themen