你准备好测试你对 react 的了解了吗?如果您不确定如何执行此操作,请不要担心 - 您可以直接跳到解决方案。我将带您详细了解整个过程,向您展示它是如何完成的!
挑战:在 React 中创建待办事项列表
挑战
在这个挑战中,您将在 React 中创建一个基本的待办事项列表应用程序。
我们已经构建了两个组件:一个TodoList
组件和一个TodoItem
组件。
该 TodoList
组件在其状态中只有一个待办事项列表,每个待办事项都有一个text
属性和一个done
属性。TodoItem
然后我们可以在组件 中渲染我们的待办事项列表 。
然后在 TodoItem
组件中,我们只渲染一个带有输入框的列表项,该输入框具有值。如果此待办事项已完成,则该值不可编辑——它是只读的。然后我们有一个按钮,我们可以用它来“完成”或“取消”任务。
现在,它在右侧渲染得很好,但我们实际上不能在TodoList
组件的状态下改变这些东西。因此,如果我尝试对这些输入元素进行更改,则什么也不会发生。如果我单击Finish,按钮上的文本不会发生应有的变化。而且我仍然可以在输入字段中选择文本,如果我真的可以进行更改,它将是可编辑的
分叉笔并尝试找出您自己的解决方案!或者继续阅读,我将在下面引导您完成。
解决方案
首先将原始CodePen分叉并给它一个不同的名称,例如添加“MY SOLUTION”。
我们需要在这里做几件不同的事情。首先,在我们的TodoItem
组件内部,我们需要能够对这些数据进行更改。这意味着我们需要有一个onClick
用于按钮的onChange
处理程序和一个用于输入元素的处理程序。
但是我们需要一种方法将这些更改传递回父TodoList
组件。这意味着TodoList
需要将一个函数传递给TodoItem
,以便它可以调用该函数。
创建一个 updateTodo 函数
所以我们现在首先添加一个带有一些虚拟文本的updateTodo
函数,TodoList
稍后我们将对其进行更新。现在我们已经创建了updateTodo
,我们可以在TodoItem
.
那么这将如何运作呢?好吧,让我们从按钮开始。我们将添加一个onClick
处理程序,我们将添加this.handleClick
.
所以现在我们需要编写我们的handleClick
函数。该handleClick
函数将对todo
传递给的属性 进行更改TodoItem
。
单击此按钮时,我们希望反转 的值done
。所以如果是假的,就切换成真,如果是真,就切换成假。然后,当然,我们希望todo
通过函数将新更新的对象传回updateTodo
。
我们可以通过做得到我们newTodo
的Object.assign
,这样我们就不会改变数据。我们将复制现有待办事项中的所有属性,实际上是this.props.todo
.
但是我们要确保我们覆盖了done
属性,它应该是相反的或负的this.props.todo.done
。
这就是我们的newTodo
. 然后我们可以做this.props.updateTodo
,我们将通过它newTodo
。
好的,这就是处理点击。现在让我们写下我们的updateTodo
now,这样我们就可以真正看到它的实际效果。现在,如果我单击Finish,您可以看到我们的新 todo 对象正在被打印出来,其中 done 设置为 true,但我们还没有看到 UI 中反映出来。那是因为现在,我们需要将这个新的保存todo
回我们的todos
状态。
设置状态
因此,让我们继续编写一个updateTodo
函数,并将其newTodo
作为其参数。在里面,我们要做this.setState
.
现在,我将以一种您以前可能没有见过的方式设置状态。我们将传递一个函数来设置状态,而不是传递一个对象。这实际上被认为是 React 中的一个很好的实践,并且可能是将来设置状态的唯一方法。您传递给的函数setState
将接收当前状态作为参数。所以我们可以接收这个状态作为这个函数的参数,然后我们从这个函数返回我们的新状态。
这实际上是一种基于旧状态创建新状态的更可靠的方法。setState
在我们的调用 中,您几乎可以将其视为一种 reducer 函数。
所以我们将继续并在这里返回一个新对象。既然这就是我们要从这个函数做的所有事情,我们实际上可以把大括号括在括号中,这样它就知道这是一个对象而不是函数块。
让我们得到我们现有的state.todos
,我们将在todo
那里映射每个,如果todo.id
等于newTodo.id
,那么我们知道这是todo
我们必须替换的对象。所以我们可以用 替换它newTodo
,否则我们可以返回旧的todo
,因为它不是我们要替换的那个。
然后我们只需要改变我们的updateTodo
函数来引用this.updateTodo
.
现在,如果您单击Finish,您会看到按钮变为Unfinish,这是因为todo.done
现在是 true 而不是 false。此外,“洗地板”文本现在有点灰显,不再可编辑。如果我单击Unfinish,它会切换回 Finish,并且文本框可以再次编辑。
使文本可编辑
我们的下一步是通过添加onChange
处理程序使文本可编辑。
在线上input
,我们将添加onChange={this.handleChange}
. 然后我们需要编写handleChange
.
我们将首先创建一个newTodo
并从中复制所有属性this.props.todo
,然后handleChange
传递我们的事件对象。我们将设置text
为e.target.value
。然后在下面我们会做this.props.updateTodo
,我们会通过它newTodo
。
所以现在,如果我们更改文本,它就可以正常工作。我们现在可以说买鸡蛋而不是牛奶,我们可以说洗车而不是地板。所以现在,我们成功地对子组件中的对象进行了更改,并将这些更改传递回父组件,在那里可以存储它们。
简化代码
所以它现在可以按照我们的意愿工作,但我还有一件事要做。你会注意到handleChange
函数和handleClick
函数有很多相似的代码。我经常有这样的子组件,我们希望以某种方式更新对象,然后将其传递回父组件,您会发现这样做的常见模式就是Object.assign
以这种方式使用。
因此,我们要做的整理工作是创建一个名为sendDelta
. 在这种情况下,增量只是在这个待办事项和我们需要的新待办事项之间需要改变的任何东西的术语。所以我们可以在这里做的就是将我们的delta
,或者我们的对象传递给需要改变的属性sendDelta
。
然后我们只需复制代码handleClick
并将其粘贴到sendDelta
. delta
基本上是我们传递给的最后一个参数,Object.assign
所以我们可以继续用 替换下面突出显示的代码delta
,然后发送它。
所以现在在handleClick
and中handleChange
,我们可以简单地将大部分代码替换为this.sendDelta
,如下所示。如您所见,它更加简洁。
所以这就是解决方案!完整的源码可以参考下图解决方案CodePen:
当然,您可能想出了不同的解决方案。如果是这样,那就太好了。无论如何,我们现在已经成功创建了一个子组件,它可以更改其数据,然后将这些更改发送回以存储到其父组件。
- 挑战
- 解决方案
- 创建一个 updateTodo 函数
- 设置状态
- 使文本可编辑
- 简化代码