拆解高频面试题:你是如何理解单向数据流的?

开发 前端
状态可以简单的理解为数据,与 props 类似,但是 state 是私有的,并且完全受控于当前组件,因此组件状态指的就是一个组件自己维护的数据。

[[422460]]

本文转载自微信公众号「勾勾的前端世界」,作者西岭 。转载本文请联系勾勾的前端世界公众号。

今天的主要内容是组件状态。

状态可以简单的理解为数据,与 props 类似,但是 state 是私有的,并且完全受控于当前组件,因此组件状态指的就是一个组件自己维护的数据。

上篇我们也提到了一个非常重要的点:数据驱动UI。意思也很简单,就是页面所展示的内容,完全是受状态控制的,这也就是所谓 MVVM 的理念。UI 的改变,全部交给框架本身来做,我们只需要管理好 “数据(状态)” 就可以了。

那么在 React 中,如何对状态进行管理呢?这就是本章节的重点,也是整个 React 学习的重点:组件的状态管理。

基本使用

state 的使用是非常简单的,我们在类中声明一个名为 state 的对象,对象中的元素就是当前组件所维护的状态数据,获取展示数据时,只需要在 jsx 中,使用 this.state.xx 的方式获取就可以了。

import React, { Component }from'react' 
 
exportclass States extends Component { 
  // 声明 state 对象 
  state = { 
    name:'xiling'
    age:18 
  } 
 
  render() { 
    return ( 
      <> 
        <h2>state 状态</h2> 
        {/* 使用 this.state.xx 获取数据 */} 
        <p>{this.state.name}</p> 
        <p>{this.state.age}</p> 
      </> 
    ) 
  } 

 
exportdefault States 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

前面我们说,state 数据是可以控制界面的,那么我们如何修改 state 从而让界面发生改变呢?

修改状态

想要修改 state 的值,最直观的方式就是直接使用 this.state={} 的方式直接修改。我们设置一个按钮,当点击按钮时,通过 this.state={} 发现是不起作用的,那应该怎么做呢?

React 给我们提供了专门的 this.setState({}) 方法,我们需要调用 this.setState({}) 方法将需要修改的数据传入才能正确的修改 state 的值。

至于为什么,需要我们理解 React 数据流才能搞懂,这里就不再详细介绍,你只需要记住这个规则就可以了。

import React, { Component }from'react' 
 
exportclass States extends Component { 
  // 声明 state 对象 
  state = { 
    name:'xiling'
    age:18 
  } 
 
  // 箭头函数 
  changes = ()=>{ 
    // console.log(22) 
    // this.state.name = 'xiling' // 错误的使用方式 
    this.setState({name:'西岭'})  
  } 
 
  render() { 
    return ( 
      <> 
        <h2>state 状态</h2> 
        {/* 使用 this.state.xx 获取数据 */} 
        <p>{this.state.name}</p> 
        <p>{this.state.age}</p> 
        <buttononClick={this.changes}>改变state</button> 
      </> 
    ) 
  } 

 
exportdefault States 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

一旦 state 的值发生了改变,那么 JSX 中使用 state 的地方就会自动发生改变。

这里也需要注意一点,因为 setState 方法是类中的属性(方法),我们需要使用 this 进行获取,因此,事件绑定的处理函数就需要使用箭头函数来固定 this 的指向,一定不要使用普通的函数 (类方法) 声明,否则会因为找不到方法而直接报错。

自顶向下的单向数据流

关于数据流的问题,是面试中高频次出现的典型题目,一般情况下面试官会直接问:“你是如何理解单向数据流的 ? ”。

注意,这不是一个单独的个体问题,而是数据流问题的综合体。解答这个问题,你需要解释:

什么是数据流?

为什么是自顶向下的?

单向数据流是什么意思?

为什么是单向的?不能是双向的数据流嘛?

单向数据流有什么作用呢?

面试题一旦拆开,你会发现面试官问出来的几乎每一个词都需要解释。宝儿,这个问题,真不简单啊!

那么,我应该怎么解答呢?

说实话,并没有标准答案,因为数据流这个问题,涉及到了框架本身的设计理念,需要你对框架的设计有深入理解,你要站在框架作者的角度看待问题;但是,对于初学者来说,这个问题显然超纲了。

完犊子,那么重要,我又学不了是嘛?不是,你需要学很多遍,这只是第一遍。

开始之前,我们先来看一段普通的 JS 代码:

var datas = { 
  name:'lisi'
  age:18 

 
var l1 = datas 
var l2 = l1 
var l3 = l2 
 
l1.age=20 
console.log(l1.age,l2.age,l3.age) // 20 20 20 
 
l3.age=26 
console.log(l1.age,l2.age,l3.age) // 26 26 26 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

你会发现,无论我们是修改那个变量的 age 属性,其他数据都会跟着改变,原因也很简单,大家都是共享一个内存数据的。

但是,赋值的前后逻辑上,我们可以将 L3 节点看作孙子,L2 节点看做父亲,L1 节点看做爷爷。

任意一个节点的数据改变之后,所有节点的数据都会跟着改变,我们就可以把这种现象看做是数据在“变量节点”上的流动。

但是,这样的数据流动,是双向的,拿 L2这个节点来说,只要数据改变,上层的 L1 节点和下层的 L3 节点都会跟着改变。

虽然这个例子并不恰当,但是回到 React 组件中,道理是一样的,所谓数据的流动就是数据在组件间的传递。前面我们用了很大的篇幅讲解的组件间的值传递,其实就是在讲数据流这个概念的具体用法。

那么,我们在数据流前面加上一个“单向”的定语,叫 “单向数据流” 是什么意思呢?其实现在你理解起来很简单,就是数据在某个节点被改变后,只会影响一个方向上的其他节点。

那所谓的自顶向下又怎么解释呢?

更简单了,就是数据只会影响到下一个层级的节点,不会影响上一个层级的节点。用上面的例子解释,就是如果 L2 数据改变,只会影响到 L3,不会影响到 L1 或者其他节点。

这就是 “自顶向下的单向数据流”。那么我们在 React 框架中,就可以明确定义单向数据流:规范数据的流向,数据由外层组件向内层组件进行传递和更新。

那么,在具体的代码实现中,是怎么体现出来的呢?翠花,上代码:

图有点看不清,接下来,我们看具体代码的演示:

// ========== App============ 
import React, { Component } from'react' 
import C1 from'./C1' 
exportclass App extends Component { 
  state = { 
    name:"xiling" 
  } 
  render() { 
    return ( 
      <div> 
        <h1>App</h1> 
        <p> APP 中的值: 
          <bstyle={{ color:"red" }}> 
            {this.state.name
          </b> 
        </p> 
        <C1toC1={this.state.name}></C1> 
      </div> 
    ) 
  } 

 
exportdefault App 
 
 
// ========== C1 ============ 
import React, { Component } from'react' 
import C2 from'./C2' 
exportclass C1 extends Component { 
 
 
  render() { 
    return ( 
      <div> 
        <h2>C1</h2> 
        <p>传入C1 的值(App传入): 
          <bstyle={{ color:"red" }}> 
            {this.props.toC1} 
          </b> 
        </p> 
        <C2toC2={this.props.toC1}></C2> 
      </div> 
    ) 
  } 

 
exportdefault C1 
 
 
// ========== C2 ============ 
import React, { Component } from'react' 
import C3 from'./C3' 
exportclass C2 extends Component { 
 
  state = { 
    name:this.props.toC2 
  } 
 
  changes = () => { 
    this.setState({ 
      name:Math.random() 
    }) 
  } 
 
  render() { 
    return ( 
      <div> 
        <h2>C2</h2> 
        <buttononClick={() => { this.changes() }}> 
          修改 
        </button> 
        <p>传入C2 的值(C1传入): 
          <bstyle={{ color:"red" }}> 
            {this.state.name
          </b> 
        </p> 
        <C3toC3={this.state.name}></C3> 
      </div> 
    ) 
 
  } 

 
exportdefault C2 
 
 
// ========== C3 ============ 
import React, { Component } from'react' 
 
exportclass C3 extends Component { 
  render() { 
    return ( 
      <div> 
        <h2>C3</h2> 
        传入C3 的值(C2传入): 
        <bstyle={{ color:"red" }}> 
          {this.props.toC3} 
        </b> 
      </div> 
    ) 
  } 

 
exportdefault C3 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.

最后,我们再来解释,为什么?有什么用?

其实这才是这个问题的核心,不同的技术理解,就会有不同的角度解释,我这里仅一家之言,你且听听罢。

我们设想这样的情景:

父组件的数据通过props传递给子组件,而子组件里更新了 props,导致父组件和其他关联组件的数据更新,UI 渲染也会随数据而更新。

毫无疑问,这是会导致严重的数据紊乱和不可控。

因此绝大多数框架在这方面做了处理。而 React 在这方面的处理,就是直接规定了 Props 为只读的,而不是可更改的。这也就是我们前面看到的数据更新不能直接通过 this.state 操作,想要更新,就需要通过 React 提供的专门的 this.setState() 方法来做。

单向数据流其实就是一种框架本身对数据流向的限制。

 

暂时先说这些吧,等我们学的越多,经验越丰富,对它的理解也就会越深刻,看待它的角度也就越全面。

 

责任编辑:武晓燕 来源: 勾勾的前端世界
相关推荐

2021-02-23 12:43:39

Redis面试题缓存

2019-11-26 10:30:11

CSS前端面试题

2021-01-22 11:58:30

MySQL数据库开发

2019-12-26 09:52:33

Redis集群线程

2021-08-05 05:04:50

热部署模型字节

2022-04-15 09:23:29

Kubernetes面试题

2022-08-22 18:57:29

React前端面试

2020-08-31 12:20:07

Python面试题代码

2023-11-15 07:54:03

HashMap数据结构

2020-03-03 17:47:07

UDP TCP面试题

2022-07-26 09:03:50

幂等性数据状态机

2021-11-02 10:10:38

面试元素语言

2021-12-08 11:18:21

Spring Bean面试题生命周期

2023-10-20 15:58:27

Python删除指定字符

2023-11-27 07:47:14

2020-06-04 14:40:40

面试题Vue前端

2014-09-19 11:17:48

面试题

2021-03-12 13:57:13

零拷贝技术

2025-03-20 07:54:57

2023-11-13 07:37:36

JS面试题线程
点赞
收藏

51CTO技术栈公众号