React组件及组件之间的数据传递及函数调用

组件

组件,指的是能完成某个特定功能的独立的、可重用的代码。Component是所有组件的基类,提供了很多组件共有的功能;

React 为每个组件提供了生命周期钩子函数去相应不同的时刻-----创建时(实例化)、存在期及销毁时。

React 组件之间通信是单向的,数据只能由一方传到另一方。

组件之间的关系有:

  1. 父组件向子组件传递值
  2. 子组件向父组件传递值
  3. 兄弟组件之间通信

父组件代码

import React, { Component } from 'react';
import { Card, Row, Col, Button, Tooltip, Tabs } from 'antd';
import Children1 from './Children1';
import Children2 from './Children2';
import Children3 from './Children3';

import Context from './ChildContext';

const { TabPane } = Tabs;

class Index extends Component {
  /**
   * constructor 构造方法
   * @param props
   * ES6 对类的默认方法,通过 new 命令生成对象实例时自动调用该方法。
   * 该方法是类必有的,如果没有显示定义,则会默认添加空的 constructor()方法
   *
   * 如果想在constructor中使用this.props,super必须加props
   */
  constructor(props) {
    // todo 如果constructor中不通过super来接收props,在其他生命周期,
    // 诸如componentWillMount、componentDidMount、render中能直接使用this.props吗??
    // 结论:可以的,react在除了constructor之外的生命周期已经传入了this.props了,完全不受super(props)的影响。
    /**
     * super(props)
     * 可以不写constructor, 一旦写constructor ,就必须在函数中写super()
     * 此时组件才有自己的this, 在组件的全局中都可以实现this关键字
     * 否则如果只是constructor 而不执行super() ,那么this 指向都是错的
     */
    super(props);
    this.state = {
      msg: '父类消息',
      name: 'parent',
      index: 1,
      child3C: { key: 'child3child' },
    };
  }

  callBack = (msg, name, index) => {
    this.setState({ msg, name, index });
  };

  /**
   * 父组件 点击调用子组件2 中的函数
   * @param e
   */
  handleClick = e => {
    e.stopPropagation();
    const { msg, name, index } = this.state;
    this.children2.handleChildren2Fn(`${msg}${index}`, `${name}${index}`);
    this.setState({ index: index + 1 });
  };

  /**
   * 点击修改父组件数据,可以相应更新子组件展示数据
   * @param e
   */
  handleChangeState = e => {
    e.stopPropagation();
    const { msg, name, index } = this.state;
    this.setState({
      msg: `${msg}${index + 1}`,
      name: `${name}${index + 1}`,
      index: index + 1,
    });
  };

  /**
   * 父组件Context上下文
   * @param e
   * Provider value 属性相当于getChildContext()
   */
  handleContextBack = e => {
    e.stopPropagation();
    // const { child3C: { key: 'child3child' }, } = this.state;
    const {
      index,
      child3C: { key },
    } = this.state;
    this.setState({
      child3C: {
        key: `${key}${index + 1}`,
      },
    });
  };

  render() {
    const { msg, name, child3C } = this.state;
    return (
      <Card bordered={false}>
        <Tabs defaultActiveKey="1" tabPosition="top">
          <TabPane tab="React父组件与子组件之间" key="1">
            <Card title="React父组件与子组件之间的数据传递及函数调用">
              <Row gutter={24}>
                <Col span={8}>
                  <div>
                    {msg}:{name}
                  </div>
                  <Tooltip placement="topLeft" title="父组件 点击调用子组件2 中的函数">
                    <Button onClick={this.handleClick}>P调用C2函数</Button>
                  </Tooltip>
                  <Tooltip
                    placement="topLeft"
                    title="点击修改父组件数据,可以相应更新子组件展示数据"
                  >
                    <Button onClick={this.handleChangeState}>P函数修改数据,同步C2数据</Button>
                  </Tooltip>
                </Col>
                <Col span={8}>
                  <Children1 {...this.state} callBack={this.callBack} />
                </Col>
                <Col span={8}>
                  <Children2
                    {...this.state}
                    ref={children => {
                      this.children2 = children;
                    }}
                  />
                </Col>
              </Row>
            </Card>
          </TabPane>
        </Tabs>
      </Card>
    );
  }
}

export default Index;

image

一、父组件 与 子组件传递数据 Parent -> Child2

父组件向子组件传递值,可以给子组件通过props传递,自上而下进行传递。

在父组件中代码,将父组件中的state中的数据通过...展开,将父组件Parent中的信息,简洁的传递到子组件。

<Children2
    {...this.state}
    ref={children => {this.children2 = children;}}
/>

我在demo中将传递函数有两个按钮,“P调用C2函数” 与 “P函数修改数据,同步C2数据”

按钮“P调用C2函数” 与 “P函数修改数据,同步C2数据”

1、 P函数修改数据,同步C2数据

<Button onClick={this.handleChangeState}>P函数修改数据,同步C2数据</Button>

这个按钮式,修改Parent数据,然后同步Children2的数据,点击按钮,调用onClick函数,此函数点击修改父组件数据,可以相应更新子组件展示数据。

父组件的props与state改变会导致子组件的生命周期发生变化,更新子组件数据。

/**
   * 点击修改父组件数据,可以相应更新子组件展示数据
   * @param e
   */
  handleChangeState = e => {
    e.stopPropagation();
    const { msg, name, index } = this.state;
    this.setState({
      msg: `${msg}${index + 1}`,
      name: `${name}${index + 1}`,
      index: index + 1,
    });
  };

在子组件children2中,可以通过this.props 获取,如下 子组件2 代码。

image
image

2、 P调用C2函数 父组件调用子组件函数 (也是子组件传递函数到父组件)

父组件 点击调用子组件2 中的函数。由于组件之间通信是单向的,父组件可以传递给子组件,但是如果父组件需要调用子组件函数,需要进行特殊处理。

子组件2中定义函数,this 指向 children2

handleChildren2Fn = (msg, name) => {
   this.setState({ msg, name });
};

在父组件

<Children2{...this.state} ref={children => {this.children2 = children;}}/>

使用ref 方法。ref 方法可以接收一个回调函数,表示子组件加载完成之后执行的方法,回调函数的参数即为子组件的作用域this,把子组件的作用域this赋值给父组件,就可以用用子组件中的方法了。

/**
   * 父组件 点击调用子组件2 中的函数
   * @param e
   */
  handleClick = e => {
    e.stopPropagation();
    const { msg, name, index } = this.state;
    this.children2.handleChildren2Fn(`${msg}${index}`, `${name}${index}`);
    this.setState({ index: index + 1 });
  };

子组件2代码

import React, { Component } from 'react';
import { Card } from 'antd';
import isEqual from 'lodash/isEqual';

class Children2 extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'children2',
      msg: 'children2 消息',
      pName: props.name,
      pMsg: props.msg,
    };
  }

  /**
   * getDerivedStateFromProps react17 新的生命周期
   * 用于替换 componentWillReceiveProps ,用来控制props 更新 state的过程,他返回一个对象表示新的state.
   * 每次渲染之前都会调用,不管初始挂载还是后面的 更新,都会调用,
   * componentWillReceiveProps 只有父组件早场重新渲染才会调用
   * 执行 setState 也会触发此函数
   *
   * static 中不允许用this
   *
   * 由于getDerivedStateFromProps 会在setState()后被调用,
   * 并且它的返回值会被用于更新数据, 这意味着会在测触发setState()
   * nextProps 第一个位置上的参数,未必是“新的” props,在组建中调用setState() 时,
   * getDerivedStateFromProps 会被调用,此时nextprops并不是最新的。
   *
   *
   * @param nextProps
   * @param preState
   * @returns {*}
   */
  static getDerivedStateFromProps(nextProps, preState) {
    if (isEqual(nextProps.msg, preState.msg)) {
      return null;
    }
    const { msg, name, index } = nextProps;
    return { pMsg: msg, pName: name, index };
  }

  handleChildren2Fn = (msg, name) => {
    // console.log('调用children2 消息=========');
    // console.log(this);
    this.setState({ msg, name });
  };

  render() {
    const { name, msg, pMsg, pName } = this.state;
    return (
      <Card title="子组件二">
        <div>
          {pMsg} : {pName}
        </div>
        <div>
          {msg} : {name}
        </div>
      </Card>
    );
  }
}

export default Children2;


二、子组件 -> 传递数据 到父组件

子组件1代码

import React, { Component } from 'react';
import { Card, Button } from 'antd';

class Children1 extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'children1',
      msg: 'children1 消息',
      pName: props.name,
      pMsg: props.msg,
    };
  }

  /**
   * handleClick 采用箭头函数的写法,目的是改变this的指向,
   * 使得在函数单独调用的时候,函数内部的this依然指向children1 组件
   * @param msg
   * @param name
   */
  handleClick = () => {
    // console.log(this);
    const { name, msg } = this.state;
    this.props.callBack(msg, name);
  };
  
  render() {
    const { name, msg, pMsg, pName } = this.state;
    return (
      <Card title="子组件一">
        <div>
          {pMsg} : {pName}
        </div>
        <div>
          {msg} : {name}
        </div>
        <Button onClick={this.handleClick}>子组件1 点击事件(子调用父函数)</Button>
      </Card>
    );
  }
}

export default Children1;

如图所示,父组件、子组件一、子组件二

image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容