[译] 深刻理解 Props 和 State

原文连接: learnreact.design/2018/01/15/…react

喜欢理由: 文笔生动 通俗易懂react-native

特别鸣谢: 原做者 Linton Ye 的倾情校对ionic

系列博客: 用通俗的语言和涂鸦来解释 React 术语post

上篇文章中,咱们介绍了组件、props 和 state 。学习

props 和 state 的区别至关明显,肯定什么时候使用 props 和 state 彷佛也很简单。举个例子,屋顶的颜色天然就是 prop ,由于颜色是屋顶的固有属性。另外一方面,门的开关状态很显然是 state ,由于门在建立后还能够打开或关上。然而在本文中,咱们未来挑战这一思惟方式!动画

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

学习目标

当你读完本文后但愿你能从新回到这里,并可以轻松回答如下问题:翻译

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

新成员

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

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

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

实现猫

如今我来问你,若是实现猫的行为?先来试试吧!

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

House:
  <div> <Roof /> <Wall /> <Window /> <Door /> <Cat /> </div>

Door:
  State: status <!-- "open""closed" -->
  <div>{state.status} door</div>
  当点击门时
    若是 door.state.status 为 "open"
      将 door.state.status 修改为 "closed"
    不然
      将 door.state.status 修改为 "open"
复制代码

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

猫要么睡、要么醒。这彷佛跟门的开关状态很相似。或许咱们一样可使用 state 来表示猫的状态:

Cat:
  State: status <!-- "sleeping""awake" -->
  <div>{state.status} cat</div>
复制代码

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

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

第一次尝试

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

Door:
  State: status <!-- "open""closed" -->
  <div>{state.status} door</div>
  当点击门时
    若是 state.status 为 "open"
      将 state.status 修改为 "closed"
      将 cat.state.status 修改为 "sleeping"     <!-- 错误的 -->
    不然
      将 state.status 修改为 "open"
      将 cat.state.status 修改为 "awake"        <!-- 错误的 -->
复制代码

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

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

第二次尝试

那么在 Cat 组件内来修改猫的状态如何?此次应该能够的,是吧?

Cat:
  State: status <!-- "sleeping""awake" -->
  <div>{state.status} cat</div>
  当点击门时                                    <!-- 黑人门号脸???怎么个点击法? -->
    若是 door.state.status 为 "open"            <!-- 错误的 -->
      将 cat.state.status 修改为 "sleeping"
    不然
      将 cat.state.status 修改为 "awake"
复制代码

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

解决办法

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

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

提高门的 state

House 组件:

House:
  <div> ... <Door /> <Cat /> </div>
复制代码

DoorCat 是并排放置的。或许这就是能够轻松同步它们的地方?

可是,咱们如今是在 House 组件内。与以前的尝试同理,在这里是没办法读取 Door 的 state 或者改变 Cat 的 state 。

但若是咱们使用 props 来替代 state 呢?

House:
  <div>
    ...
    <Door status="open" />
    <Cat status="awake" />
  </div>
复制代码

当门关上时:

House:
  <div>
    ...
    <Door status="closed" />
    <Cat status="sleeping" />
  </div>
复制代码

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

House:
  <div>
    ...
    <Door status={doorStatus} />
    <Cat status={若是 doorStatus 为 'open' 值为 'awake' 不然为 'sleeping'} />
  </div>
复制代码

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

House:
  State: doorStatus <!-- 'open''closed' -->
  <div>
    ...
    <Door status={state.doorStatus} />
    <Cat status={若是 state.doorStatus 为 'open' 值为 'awake' 不然为 'sleeping'} />
  </div>
复制代码

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

咱们还须要修改 DoorCat 组件,使用 props 来代替 state :

Door:
  <div>{props.status} door</div>
Cat:
  <div>{props.status} cat</div> 
复制代码

正如你所见,由于咱们想要使用来父组件的 state,在这种状况下,为了设置猫的状态,门的状态实际上是来自于 House 的,咱们能够将相同的数据表示为父组件的 state,并将数据做为 props 传递给子组件。一般,这被称之为 state 提高。咱们将 state 移至组件的更高层级处。

更改房子的 state

如今门和猫的状态经过房子的 state 进行链接。若是想开门或唤醒猫的话,咱们须要更改 House 组件的 state 。

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

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

因此 Door 组件须要作些改动:

Door:
  <div>{props.status} door</div>
  当点击门时 
    作某件事来修改 `House` 的 state
复制代码

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

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

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

House:
  State: doorStatus <!-- 'open''closed' -->
  toggleDoorStatus:
    若是 state.doorStatus 为 'open'
      将 state.doorStatus 修改为 'closed'
    不然
      将 state.doorStatus 修改为 'open'
  ...
复制代码

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

而后将 toggleDoorStatus 做为 prop 传递给 Door 组件:

House:
  ...
  <div>
    ...
    <Door ... onClickAction={toggleDoorStatus} />
    ...
  </div>
复制代码

Door 组件中,咱们只需执行这个点击操做便可:

Door:
  <div>{props.status} door</div>
  当点击门时 
    执行 props.onClickAction <!-- 实际运行的是名为 "toggleDoorStatus" 的代码-->
复制代码

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

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

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

House:
  State: doorStatus <!-- 'open' or 'closed' -->
  toggleDoorStatus:
    若是 state.doorStatus 为 'open'
      将 state.doorStatus 修改为 'closed'
    不然
      将 state.doorStatus 修改为 'open'
  <div>
    ...
    <Door status={state.doorStatus} onClickAction={toggleDoorStatus} />
    <Cat status={若是 state.doorStatus 为 'open' 值为 'awake' 不然为 'sleeping'} /> </div>
Door:
  <div>{props.status} door</div>
  当点击门时
    执行 props.onClickAction
Cat:
  <div>{props.status} cat</div>  
复制代码

再次审视 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 发生改变,旧的那只猫就会消失,而后一只崭新状态的猫将从新建立。可是这一过程发生的很是之快,这就形成了咱们的视觉残留,认为只有一只猫在那睡了又醒。

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

总结

好了,咱们经过一个更复杂的示例再次学习了 props 和 state 。在这个示例中,当点击门时,门须要切换开关状态,同时咱们还须要将门和猫的状态保持同步,

由于 state 是私有的,因此咱们须要将门的状态从 Door 组件提高到 House 组件中。这样咱们即可以在 House 组件中使用此数据来设置门和猫的状态。咱们将此数据做为 props 传给 DoorCat,以便它们根据门的状态来显示正确的图片。

另外一个需求是点击门时触发状态的变动。由于如今门的状态是 House 的 state,它是私有数据,只能在 Door 组件中间接来更改它。咱们在 House 组件中编写了实际修改 state 的代码,而后将其做为 props 传递给 Door 。这相似于把电视的遥控器传给别人。

本文中的示例或许会让你感到一丝困惑。下面是一种用来思考 prop 和 state 的更实用的方式:

  • State: 若是 UI 须要更改就表示某处确定会有 state
  • Props: 用来传递数据、传递控制

到目前为止感受如何?若是你有任何问题或意见,请给我留言!

相关文章
相关标签/搜索