让我们继续扩展我们的应用,允许用户添加新的便笺。
先将notes存储在App的状态中

  1. import React, { useState } from 'react'
  2. import Note from './components/Note'
  3. const App = (props) => {
  4. const [notes, setNotes] = useState(props.notes)
  5. const addNote = (event) => {
  6. event.preventDefault()
  7. console.log('button clicked', event.target)
  8. }
  9. return (
  10. <div>
  11. <h1>Notes</h1>
  12. <ul>
  13. {notes.map(note =>
  14. <Note key={note.id} note={note} />
  15. )}
  16. </ul>
  17. <form onSubmit={addNote}>
  18. <input />
  19. <button type="submit">save</button>
  20. </form>
  21. </div>
  22. )
  23. }
  24. export default App;

表单的submit默认会导致页面刷新,event.preventDefault()阻止页面刷新

让我们添加一个名为 newNote 的新状态,用于存储用户提交的输入,让我们将它设置为input 元素的value 属性:

  1. -- snip --
  2. const App = (props) => {
  3. const [notes, setNotes] = useState(props.notes)
  4. const [newNote, setNewNote] = useState(
  5. 'a new note...'
  6. )
  7. -- snip --
  8. return (
  9. <div>
  10. -- snip --
  11. <form onSubmit={addNote}>
  12. <input value={newNote} />
  13. <button type="submit">save</button>
  14. </form>
  15. </div>
  16. )
  17. }
  18. export default App;

此时input 不能编辑输入文本。 而且控制台出现了一个警告
image.png
由于我们将App 组件的一部分状态指定为 input 元素的value 属性,因此App 组件现在控制 了input 元素的行为。
为了能够编辑 input 元素,我们必须注册一个事件处理 来同步对 input 所做的更改和组件的状态:

  1. -- snip --
  2. const handleNoteChange = (event) => {
  3. console.log(event.target.value)
  4. setNewNote(event.target.value)
  5. }
  6. -- snip --
  7. <input
  8. value={newNote}
  9. onChange={handleNoteChange}
  10. />

安装的React Devtool 可以查看到state的值
image.png
修改addNote, 将新的note添加进notes

  • 用concat创建新的数组
  • 最后setNewNote(‘’)将input的值清空

    1. const addNote = (event) => {
    2. event.preventDefault()
    3. const noteObject = {
    4. content: newNote,
    5. date: new Date().toISOString(),
    6. important: Math.random() < 0.5,
    7. id: notes.length + 1,
    8. }
    9. setNotes(notes.concat(noteObject))
    10. setNewNote('')
    11. }

    以上将input的value与App组件的状态绑定,并添加onChange事件更新状态的方法,称为受控组件

    Filtering Displayed Elements 过滤显示元素

  • 新建一个showAll状态存储是否过滤

  • 用filter方法过滤,返回符合回调函数返回值为true的元素组成的数组 ```javascript — snip —

const App = (props) => { — snip — const [showAll, setShowAll] = useState(true)

const notesToShow = showAll ? notes : notes.filter(note => note.important)

— snip —

return (

Notes

    {notesToShow.map(note => )}
— snip — ) }

export default App;

  1. <a name="fUpao"></a>
  2. ### exercise 2.6 - 2.10
  3. 有时,为了调试目的,将状态和其他变量作为文本渲染出来会很有用。 您可以临时向渲染的组件添加如下元素:
  4. ```javascript
  5. <div>debug: {newName}</div>

phonebook实现添加姓名

  1. import React, { useState } from 'react'
  2. const App = () => {
  3. const [persons, setPersons] = useState([
  4. { name: 'Arto Hellas' }
  5. ])
  6. const [newName, setNewName] = useState('')
  7. const handleNameChange = (event) => {
  8. setNewName(event.target.value)
  9. }
  10. const handleSubmit = (event) => {
  11. event.preventDefault()
  12. setPersons([...persons, { name: newName }])
  13. }
  14. return (
  15. <div>
  16. <div>debug: {newName}</div>
  17. <h2>Phonebook</h2>
  18. <form onSubmit={handleSubmit}>
  19. <div>
  20. name: <input value={newName} onChange={handleNameChange} />
  21. </div>
  22. <div>
  23. <button type="submit">add</button>
  24. </div>
  25. </form>
  26. <h2>Numbers</h2>
  27. {persons.map(item => <div key={item.name}>{item.name}</div>)}
  28. </div>
  29. )
  30. }
  31. export default App

当名称重复时,给出提醒

  1. const handleSubmit = (event) => {
  2. event.preventDefault()
  3. const names = persons.map(item => item.name)
  4. if (names.indexOf(newName) >= 0) {
  5. alert(`${newName}已存在`)
  6. } else {
  7. setPersons([...persons, { name: newName }])
  8. }
  9. }

实现过滤姓名

  1. const handleFilterName = (event) => {
  2. const name = event.target.value.toLowerCase()
  3. const names = persons.filter(item => item.name.toLowerCase().indexOf(name) >= 0)
  4. setFilterPersons(names)
  5. setFilterFlag(true)
  6. }

image.png
思考:到目前为止,state和方法都是在App组件里,子组件都只是调用App组件的数据和方法,这有什么优点和缺点?