当前位置 : 主页 > 极光资讯 > 技术文章 >

深入理解 Props 和 State

栏目分类:技术文章  发布日期:2018-11-09    作者:哒哒

props 和 state 的区别相当明显,确定何时使用 props 和 state 似乎也很简单。举个例子,屋顶的颜色自然就是 prop ,因为颜色是屋顶的固有属性。另一方面,门的开关状态很显然是 state ,因为门在创建后还可以打开或关上。然而在本文中,我们将来挑战这一思维方式!

没开玩笑?!?没错,你所看到的东西既可以是 prop,又可以是 state 。并没有绝对的界限。我将介绍一种更有用、偏实战的方式来思考 prop 和 state 。

学习目标

当你读完本文后希望你能重新回到这里,并能够轻松回答以下问题:

  • props 和 state 的主要用途是什么?
  • “state 提升”的含义是什么?在什么场景下需要提升 state ?

新成员

你注意到房子周围的新成员了吗?试试点击房门!

查看由 focuser (@focuser) 在 CodePen 编写的 Demo : 有猫的 React 小屋。

这是一只嗜睡的猫,门一关她就睡,只有当门再开启的时候才会起来。如果把门关上,她立即又睡过去了。

实现猫

现在我来问你,如果实现猫的行为?先来试试吧!

先从下面的“代码”入手,花点时间先读一遍。(再次重申,这并非真正的 JavaScript 代码,它只是以一种简化的形式来帮助你理解概念,同时不会被 JS 中的细节所干扰。)

在 House 组件中又新增了 Cat 标签。那么 Cat 组件又是怎么样的呢?我们来定义它。

猫要么睡、要么醒。这似乎跟门的开关状态很类似。或许我们同样可以使用 state 来表示猫的状态:

Cat 组件定义好后,还需要实现的就只剩下将猫和门的状态进行同步。门的状态为 “open” 时,我们想要猫的状态为 “awake”,反之为 “sleeping” 。

就这么简单?看看再说吧…

第一次尝试

既然我们已经有了根据当前状态切换门状态的代码,莫不如我们就在此处切换猫的状态:

不幸的是,这不起作用!还记得组件的 state 是私有数据吗?只有在组件的内部才能访问。其他组件,无论是父组件还是兄弟组件,都无法访问本组件的 state 。

很遗憾,我们在 Door 组件内尝试修改猫的状态以失败告终。(转换成真正的 JavaScript 代码也不例外)

第二次尝试

那么在 Cat 组件内来修改猫的状态如何?这次应该可以的,是吧?

毫无疑问,在 Cat 组件内修改猫的状态是没问题的。但我们需要读取门的状态来决定猫的状态是什么。门的状态是 Door 组件的 state ,因此无法在 Cat 组件里访问!

解决办法

呃!太蹩脚了。要保持门和猫的状态同步,我们必须要在某处能同时访问两者。但看上去数据是通过设计而对外隐藏的。如果来解决此难题呢?

解决办法就是需要我们灵活地理解 state 和 props 的用法。

提升门的 state

House 组件:

Door 和 Cat 是并排放置的。或许这就是可以轻松同步它们的地方?

但是,我们现在是在 House 组件内。与之前的尝试同理,在这里是没办法读取 Door 的 state 或者改变 Cat 的 state 。

但如果我们使用 props 来替代 state 呢?

当门关上时:

当然,门的状态不会是固定的值,它会随时间而改变。我们用 doorStatus 来表示门的状态。

这不就解决同步的问题了嘛。顺便问下,这个会变化的值 doorStatus 是什么?在组件中什么是可以改变的?没错,正是 state 。

太棒了!House 组件现在定义的很好,门和猫的状态也能完美同步。

我们还需要修改 Door 和 Cat 组件,使用 props 来代替 state :

正如你所见,因为我们想要使用来父组件的 state,在这种情况下,为了设置猫的状态,门的状态其实是来自于 House 的,我们可以将相同的数据表示为父组件的 state,并将数据作为 props 传递给子组件。通常,这被称之为 state 提升。我们将 state 移至组件的更高层级处。

更改房子的 state

现在门和猫的状态通过房子的 state 进行连接。如果想开门或唤醒猫的话,我们需要更改 House 组件的 state 。

问题来了,哪里是唯一可以更新 House 的 state 的地方?就在 House 组件内,没错吧?

但是,我们想要在 Door 里来触发这次更改。也就是说,我们想要的效果是只有当点击门时才开门,而不是点击整个房子或窗户等。

所以 Door 组件需要做些改动:

但等等,之前不是说不能在 Door 组件内修改 House 的 state 吗?

没错。我们没办法直接修改 House 的 state 。但并不等于说不能间接地修改。看下面…

在 House 组件内,我们来写代码以实际修改它的 state :

此刻,我们还未指定何时运行这段代码。我们只是给了它一个名字 (toggleDoorStatus),以便稍后通过名称来找到它运行。

然后将 toggleDoorStatus 作为 prop 传递给 Door 组件:

在 Door 组件中,我们只需执行这个点击操作即可:

这就像把电视遥控器传递给其他人一样。某人在 Door 组件内按下了遥控器按键。House 组件里的电视机就会换台或加大音量。

将会发生什么取决于传给 Door 的遥控器是什么。它可能控制的是房间里的电视、空调或高保真音响系统。在 Door 组件内,某人需要做的只是按下遥控器的按键。

这就是我们所需要的!下面是完整“代码”:

再次审视 Props 和 State

现在让我们重温几个问题,props 和 state 的区别是什么?何时应该使用 state ?何时应该使用 props ?

何时使用 state ? 何时使用 props ?

如果你还记得的话,我曾说过 props 是组件的固有属性,它是不会改变的,而 state 是组件创建后才有的,它是可以改变的。当最初学习这两个概念时,它是有帮助的。

但是,我们刚刚创建的示例让这一观点变得令人困惑。无论是门的开的,还是猫是睡着的,这理所应当应该是 state ,但我们却使用 props 来表示它们。这是为什么?

事实证明,在 state 和 props 的选择问题上,还是有很大的灵活性的。这取决于你看它的视角,你可以采用不同的方式来为组件建模。例如,当门敞开之时,你可以说它是门的状态,你可以说它是房子的状态。

一种更有用的理解方式

感到困惑了?这是一种更有用的思考问题的方式:

  • State: 如果 UI 需要更改就表示某处肯定会有 state
  • Props: 用来传递数据、传递控制

当应用运行时,如果 UI 需要变化,那它一定是 state 。当点击门时,门或开或关,那么它一定是某处的 state 。

但是,state 并不一定是更新组件的 state 。它可能位于某个上游组件中。这完全都取决于我们需要在何处以及如何使用这些信息。举个例子,我们决定将门的状态从 Door 组件提升到 House 组件中,是因为我们需要在 House 组件中使用它。

另一方面,props 只是用来向下传递数据的东西。就像我们之前将门的状态从 House 组件中向下传递给 Door 组件。

props 还可以用来向下传递控制。例如,我们将事件处理方法从 House 传给 Door 。

本示例中的 props 是否会改变值?

并没有,props 永远不会改变值。我懂你的意思,门开开关关,猫睡了又醒。因为我们现在使用 props 来表示它们,很容易让人认为 props 的行为就好比 state,它们的值改变了,是这样吗?

这只是一个错觉,我发现它与翻页书的动画相当相似。

每次房子的 state 发生改变,旧的那只猫就会消失,然后一只崭新状态的猫将重新创建。但是这一过程发生的非常之快,这就造成了我们的视觉残留,认为只有一只猫在那睡了又醒。

翻书中任何页面上的草图都不会移动。类似的,每只猫在其(短暂的)生命中始终保持清醒/沉睡。

手机批发 手机批发