• React组件之间的通信方式总结(下)


    一、写一个时钟

    • 用 react 写一个每秒都可以更新一次的时钟
    import React from 'react'
    import ReactDOM from 'react-dom'
    
    
    function tick() {
        let ele = <h1>{ new Date().toLocaleTimeString() }</h1>
    
        // Objects are not valid as a React child (found: Sun Aug 04 2019 20:34:51 GMT+0800 (中国标准时间)). If you meant to render a collection of children, use an array instead.
        // new Date() 是一个对象数据类型的值,React 元素不接收对象作为其子元素
        ReactDOM.render(ele, document.querySelector('#root'))
    }
    
    tick()
    
    setInterval(tick, 1000) // 如果不包在一个函数中,时钟是不会每秒更新一次
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    但是 React 和 Vue 相同都是数据驱动的,但是这个时候和数据驱动没啥关系,每隔1秒钟重新创建一个 ele,然后再渲染到页面中,视图才发生变化;为了使用数据驱动,我们需要使用 React 的组件

    二、React 的组件

    在 React 组件中,jsx 元素(也称 react 元素)是组件的基本组成单位
    在 react 中定义组件有两种方式:

    1. 函数(function)定义组件
    2. 类(class)定义组件
    • 定义组件的要求:
      1. 组件的名字首字母必须大写,为了在写 jsx 时区分原生 html 标签
      1. 组件定义后,就可以当做一个标签在 jsx 语法中使用
      1. 如果使用函数定义组件必须返回一个 jsx 元素

    2.1 React 的函数组件

    react 使用函数定义组件,就是声明一个函数;

    • 函数接收一个 props 参数;props 是对象,是在渲染或者父组件通过 prop(属性) 传递过来的数据;
    • 函数返回一个 jsx 元素,在组件中需要的数据可以通过 props 传入;
    // 1. 函数定义组件
    function Welcome(props) {
        // props 是一个对象,是使用组件时,写在组件行内的属性和属性值组成的;
        console.log(data)
        return (<div>
            <p>{props.data.name}; {props.data.age}</p>
            <p>{props.x}</p>
        </div>)
    }
    
    ReactDOM.render(<Welcome data={{name: 'mabin', age: 18}} x='hahah' />, document.querySelector('#root'));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • ReactDOM.render() 会根据第一个参数的类型不同执行不同的操作;
      1. 如果是组件,当 render 执行时,首先会把当前组件的行内属性进行打包封装,把其封装成一个对象,把这个对象传给组件函数
      1. 执行组件函数,获取对应的虚拟 DOM 对象
      1. 把虚拟 DOM 转成真实 DOM 对象,并且插入到真实的 DOM 中

    2.2 React 的 class 组件

    通过 class 定义一个组件

    1. 通过 class 来定义一个组件,需要继承 React 上的 Component 这个类
    2. 在定义组件上的原型上必须有一个 render 函数,且 render 函数需要返回一个顶级的 jsx 元素

    -看🌰

    class Header extends Component {
        constructor () {
            super()
        }
    
        render () {
            // 在 render 函数中通过 this.props 访问 props
            return (<div>
                {this.props.content}
            </div>)
        }
    }
    
    class Hello extends Component {
        constructor (props) {
            super()
            // 注意在构造函数中不能访问 this.props ,props 会作为形参传入
        }
    
        render () {
            return (
                <div>
                    <Header content="现在是北京时间:" />
                    <p>{this.props.data.toLocaleString()}</p>
                </div>
            )
        }
    }
    
    // 使用这个组件
    ReactDOM.render(<Hello data={new Date()} />, document.getElementById('root'));
    
    • 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
    • ReactDOM.render() 渲染 class 声明的组件过程:
      1. 找到组件对应的类,然后 new 一下这个类,获得这个类的一个实例
      1. 通过实例找到当前类原型上的 render 函数,让 render 执行接收其返回的虚拟 DOM
      1. 将上一步的虚拟 DOM 转换成成真实 DOM ,插入到页面中

    2.3 class 和 function 定义的组件有什么不同

    React 也是数据驱动的,当数据发生变化时,视图就会自动发生变化(视图是数据的映射)。组件中的数据有两个来源:props 和 state,其中 props 就是组件被使用时接收的行内属性,是从外部传入的数据,而 state 是组件的私有数据,组件定义时就需要创建;

    1. class 定义的组件中有 this,state,生命周期的钩子,而 function 声明的组件只有 props;

    三、数据映射视图

    3.1 属性(props)映射视图

    属性(prop)也是组件的数据,而视图是数据的映射,当数据发生变化,组件会自动重新渲染

    -看🌰

    function Welcome(props) {
        return <div>{props.time.toLocaleString()}</div>
    }
    
    
    setInterval(() => {
        // 每隔一秒钟 new Date的值会发生变化,即 Welcome 的 time prop 属性发生了变化,而视图自动变化
        let now = new Date()
        ReactDOM.render(<Welcome time={now} />, document.querySelector('#root'))
    }, 1000)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    -看🌰

    把数据通过属性传递给组,参考 前端react面试题详细解答

    function User(props) {
        console.log(props)
        let { name, age } = props;
        return <div>
            <p>{name}</p>
            <p>{age}</p>
        </div>
    }
    
    let data = {
        name: 'mabin',
        age: 18
    }
    
    // ReactDOM.render(, document.getElementById('root'))
    
    ReactDOM.render(<User {...data}/>, document.getElementById('root')) // 可以使用展开运算符把一个对象传给组件的props,等效于上面的写法
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.2 状态(state) 映射视图

    react 组件的数据有两个来源:props 和 state
    属性(props):是父组件传递过来的
    状态(state): 是组件自己管控的状态,状态是组件私有的数据

    3.2.1 使用 state
    • 在 React 中如果使用 state 必须使用 class 创建组件;
    • 在 constructor 中初始化一个状态;通过 this.state 赋值一个对象的形式初始化;
    • state 中的数据不可以直接修改,如果要更新数据,需要调用 setState 方法 ,setState 方法会进行合并 setState有两种写法 一种是对象一种是函数,如果下一个状态依赖上一个状态,我们需要使用函数的形式
      • 函数: this.setState((prevState) => {})
      • 对象: this.setState({num: 5})
    • state 发生改变后触发 render 函数执行更新 DOM
    3.2.2 在 react 中绑定事件
    • react 绑定事件时,需要使用驼峰命名法的事件名 onClick = { 事件处理函数 }
    • 在定义事件函数时,一般把事件函数声明在原型上,而绑定事件时,通过 this.add 访问这个事件函数
    • 示例:

    我们来写一个计数器感受一下 React 的数据驱动

    class Count extends Component {
      constructor () {
        super()
    
        // 在 constructor 中初始化一个状态;通过this.state 赋值一个对象的形式初始化;
        // 只有用类声明的组件才有 state
        this.state = {
          num: 1,
          x: 2
        }
        // this.add = this.add.bind(this)
      }
    
    
    
      add = () => {
        // 在 react 中如果要修改 状态只能通过 this.setState() 方法修改
        // setState 方法会进行合并 setState 有两种写法 一种是对象一种是函数
    
        // 1. setState 可以接受一个回调,回调需要 return 一个新的 state 对象,新的对象中只需包含要修改的 属性即可,例如这里我们要修改 num,return 的对象只需要包含num不用包含 x,react 会自动合并
        // 如果下一个状态依赖上一个状态,我们需要使用函数的形式
        /*this.setState((prevState) => {      console.log(prevState); // prevState 之前的状态对象      return {        num: prevState.num + 1      }    })*/
    
        // 2. setState 还可以接受一个对象,对象中需要包含要更新的 state 属性;
        this.setState({
          num: this.state.num + 1
        })
    
        // 我们发现,我们更新数据后,页面中使用 num 的地方的值也自动跟着改了;
        // react 同样是数据驱动的,当我们调用 setState 修改 state 时,react 会重新调用 render 函数,得到虚拟DOM 然后调用 DOM-diff 算法,把修改的那一部分重新渲染;
      }
    
      render () {
        // react 绑定事件时,需要使用驼峰命名法的事件名 onClick = { 事件处理函数 }
    
        // 在定义事件函数时,一般把事件函数声明在原型上,而绑定事件时,通过 this.add 访问这个事件函数
    
        return (<div>
          <p>NUM: {this.state.num} </p>
          <p>X: {this.state.x} </p>
          <button onClick={this.add}>给num加1</button>
        </div>)
      }
    }
    
    ReactDOM.render(<Count />, document.getElementById('root'))
    
    • 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

    四、属性(props) 校验

    和 Vue 的 props 一样,React 的 props 同样支持校验;React 的 props 校验需要三方的库 prop-types

    4.1 安装 prop-types

    yarn add prop-types --save
    
    • 1

    4.2 使用

    使用 类型校验需要 在 class 创建组件时创建静态属性 propTypes,值是一个对象,对象的属性是需要校验的 属性,值对应的是校验规则;

    • 类型校验看🌰
    static propTypes = {
        name: PropType.string.isRequired, // 要求 name 是字符串类型 isRequired 表示必传
        age: PropType.number.isRequired // 要求 age 是数字类型,isRequired 表示必传
    }
    
    • 1
    • 2
    • 3
    • 4
    • 此外,还可以给 prop 设置默认值,同样是通过类的静态属性设置,在创建组件时需要配置 defaultProps 静态属性;该属性的值是一个对象,该对象中属性是要设置默认值的 prop,值是 prop 的默认值
    static defaultProps = {
        name: '珠峰',
        age: 10
     }
    
    • 1
    • 2
    • 3
    • 4
    • 完整🌰
    import React, { Component } from 'react'
    import ReactDOM from 'react-dom'
    import PropType from 'prop-types'
    // React 的props 同样可以校验,但是需要一个第三方的库 prop-types
    
    class User extends Component {
        constructor (props) {
            super()
    
            console.log(props) // 对象,把行内属性封装到一个对象中
    
            // props.name = 123 // 如果想对 props 进行修改,可以在 constructor 中进行修改
        }
    
        static propTypes = {
            name: PropType.string.isRequired, // 要求 name 是字符串类型 isRequired 表示必传
            age: PropType.number.isRequired // 要求 age 是数字类型,isRequired 表示必传
        }
    
        // 给props 设置默认值
        static defaultProps = {
          name: '京东',
        age: 19
      }
    
        render () {
            return (<div>
                <p>{ this.props.name }</p>
                <p>{ this.props.age }</p>
            </div>)
        }
    }
    
    let obj = {
        name: '张三',
        age: 18
    };
    
    ReactDOM.render(<User {...obj} />, document.querySelector('#root'));
    
    • 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

    五、父子组件通信

    5.1 父传子

    在 React 中,父组件把数据传递给子组件,仍然是通过 props 的方式传递;

    -看🌰

    import React, { Component } from 'react'
    import ReactDOM from 'react-dom'
    
    class Header extends Component {
      render () {
        return (<h1>
          <p>{this.props.data}</p>
        </h1>)
      }
    }
    
    // 此时的 Panel 是父组件而 Header 是子组件,父子组件通信时父传子,仍然是通过 props 传递的
    class Panel extends Component {
      render () {
        return (<div className="container">
          <p>{this.props.news}</p>
          <Header data={this.props.min} />
        </div>)
      }
    }
    
    let data = {
      news: '快下课了',
      min: '拖几分钟'
    }
    
    ReactDOM.render(<Panel {...data} />, document.getElementById('root'))
    
    • 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

    5.2 子传父

    在 React 中子组件修改父组件的方式和 Vue 不同;子组件如果想修改父组件的数据,父组件在使用子组件的时候,通过 props 传给子组件一个可以修改父组件的方法,当子组件需要修改父组件的数据时,通过 this.props 找到这个方法执行对应的方法

    -看🌰

    import React, { Component } from 'react'
    import ReactDOM from 'react-dom'
    
    import 'bootstrap/dist/css/bootstrap.css'
    
    class Panel extends Component {
      static defaultProps = {
        a: 1
      }
      constructor () {
        super()
    
        this.state = {
          color: 'success'
        }
      }
    
      changeColor = (color) => {
        this.setState({
          color
        })
      }
    
      render () {
        return (<div className="container">
          <div className={`panel panel-${this.state.color}`}>
            <div className="panel-heading">
              {this.props.head}        </div>
            <div className="panel-body">
              {this.props.body}        </div>
            {/*通过 modifyColor 这个 props 把 Panel 组件的 changeColor 方法传递给 Footer 组件*/}        <Footer type={this.state.color}
                    modifyColor={this.changeColor} />
          </div>
        </div>)
      }
    }
    
    class Footer extends Component {
      change = () => {
        this.props.modifyColor('danger')
      }
      render () {
    
        return (<div className="panel-footer">
          <button className={`btn btn-${this.props.type}`} onClick={this.change}>变色</button>
        </div>)
      }
    }
    
    ReactDOM.render(<Panel head="头信息" body="信息主体"/>, document.getElementById('root'))
    
    // React 同样是单向数据流,即数据只能通过只能从父组件流向子组件
    // 所以子组件如果想修改父组件的数据,父组件在使用子组件的时候,通过props传给子组件一个可以修改父组件的方法,当子组件需要修改父组件的数据时,通过this.props 找到这个方法执行对应的方法就可以了
    
    • 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
  • 相关阅读:
    什么是SMTP?它是如何工作的?第1部分
    计算机专业毕业设计项目推荐13-超酷炫轻食平台(Go/Java+Vue+Mysql)
    CDGA考试-2022年最新模拟题一套100道题(含答案)
    面试打底稿③ 专业技能的第三部分
    js字符串的删除和修改
    又想到一个不入流的小算法
    用idea工具scala 和 Java开发 spark案例:WordCount
    时序数据库-4-[IoTDB]的docker安装和python3操作
    前端使用 Konva 实现可视化设计器(15)- 自定义连接点、连接优化
    Python 操作redisearch
  • 原文地址:https://blog.csdn.net/beifeng11996/article/details/127400466