05react基础-组件通讯

组件的props

  • 组件时封闭的,要接受外部数据应该通过props来实现
  • props的作用:接收传递给组件的数据
  • 传递数据:给组件标签添加属性
<Hello name="rose" age={19} 

接收数据:函数组件通过 参数 props接收数据

类组件通过 this.props接收数据
import React from 'react'
import ReactDOM from 'react-dom'
// 2 接收数据
class Hello extends React.Component {
  render() {
    // console.log(this.props)
    return (
      <div>
        <h1>props: {this.props.age}</h1>
      </div>
    )
  }
}
// 1 传递数据
ReactDOM.render(<Hello name="rose" age={19} />, document.getElementById('root'))

函数组件获取(props)接收数据

// 2 接收数据
const Hello = props => {
  // props是一个对象
  console.log(props)
  return (
    <div>
      <h1>props:{props.name}</h1>
    </div>
  )
}

// 1 传递数据
ReactDOM.render(<Hello name="jack" age={19} />, document.getElementById('root')) 
props特点
  • 可以给组件传递任意类型的数据
  • props是只读属性不能对值进行修改
  • 注意:使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法在构造函数中获取到props其他的地方是可以拿到的
import React from 'react'
import ReactDOM from 'react-dom'

/* 
  props
*/

// 类组件:
class Hello extends React.Component {
  // 推荐使用props作为constructor的参数!!
  constructor(props) {
    super(props)

    // console.log(this.props)
    console.log(props)
  }

  render() {
    console.log('render:', this.props)

    return (
      <div>
        <h1>props:</h1>
      </div>
    )
  }
}
/* 
const Hello = props => {
  console.log('props:', props)
  props.fn()

  // 修改props的值:错误演示!!!
  // props.name = 'tom'

  return (
    <div>
      <h1>props:</h1>
      {props.tag}
    </div>
  )
}
 */
ReactDOM.render(
  <Hello
    name="rose"
    age={19}
    colors={['red', 'green', 'blue']}
    fn={() => console.log('这是一个函数')}
    tag={<p>这是一个p标签</p>}
  />,
  document.getElementById('root')
)

组件通讯的三种方式

1.父组件传递数据给子组件

  • 父组件提供要传递的state数据
  • 给子组件标签添加属性,值为state中的数据
  • 子组件中通过props接收父组件中传递的数据
父组件(自定义属性):
// 父组件
class Parent extends React.Component {
  state = {
    lastName:'张'
  }

  render() {
    return (
      <div className="parent">
        父组件:
        <Child  name={this.state.lastName}/>
      </div>
    )
  }
}
子组件接收(props):
// 子组件
const Child = (props) => {
  return (
    <div className="child">
      <p>子组件,接收到父组件的数据:{props.name}</p>
    </div>
  )
}

2.子组件传递数据给父组件

  • 利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数
  • 父组件提供一个回调函数,用来接收数据
  • 将该函数作为属性的值,传递给子组件
父组件(提供回调函数,用来接收数据):
class Parent extends React.Component {
  state = {
    parentMsg: ''
  }

  // 提供回调函数,用来接收数据
  getChildMsg = data => {
    console.log('接收到子组件中传递过来的数据:', data)

    this.setState({
      parentMsg: data
    })
  }

  render() {
    return (
      <div className="parent">
        父组件:{this.state.parentMsg}
        <Child getMsg={this.getChildMsg} />
      </div>
    )
  }
}

子组件:

class Child extends React.Component {
  state = {
    msg: '刷抖音'
  }

  handleClick = () => {
    // 子组件调用父组件中传递过来的回调函数
    this.props.getMsg(this.state.msg)
  }

  render() {
    return (
      <div className="child">
        子组件:{' '}
        <button onClick={this.handleClick}>点我,给父组件传递数据</button>
      </div>
    )
  }
}

兄弟组件传递

  • 将共享状态(数据)提升到最近的公共父组件中,由公共父组件管理这个状态
  • 这个称为状态提升
  • 公共父组件职责:1. 提供共享状态 2.提供操作共享状态的方法
  • 要通讯的子组件只需要通过props接收状态或操作状态的方法


    image.png

示例:

定义布局结构,一个Counter里面包含两个子组件,一个是计数器的提示,一个是按钮

class Counter extends React.Component {
    render() {
        return (<div>
            <Child1 />
            <Child2 />
        </div>
        )
    }
}
class Child1 extends React.Component {
    render() {
        return (
            <h1>计数器:</h1>
        )
    }
}
class Child2 extends React.Component {
    render() {
        return (
            <button>+1</button>
        )
    }
}
  • 步骤一:在父组件里定义共享状态,把这个状态传递给第一个子组件
class Counter extends React.Component {
    // 提供共享的状态
    state = {
        count: 0
    }
    render() {
        return (<div>
            {/* 把状态提供给第一个子组件 */}
            <Child1 count={this.state.count}/>
            <Child2 />
        </div>
        )
    }
}
  • 步骤二:在第一个子组件里面就能通过props获取到
class Child1 extends React.Component {
    render() {
        return (
            <h1>计数器:{this.props.count}</h1>
        )
    }
}
  • 步骤三:在父组件中提供共享方法,通过属性传递给第二个子组件,方便第二个子组件来进行调用
// 提供共享方法
    onIncrement = (res) => {
        // 只要第二个子组件调用了这个函数,就会执行里面代码
        this.setState({
            count: this.state.count + res
        })
    }
    render() {
        return (<div>
            ...
            {/* 把共享方法提供给第二个子组件 */}
            <Child2 onIncrement={this.onIncrement} />
        </div>
        )
    }
  • 步骤四:在第二个子组件里面通过props来获取到对应函数,然后进行调用
class Child2 extends React.Component {
    handleClick = () => {
        // 这里一旦调用,就会执行父组件里面 onIncrement函数
        this.props.onIncrement(2)
    }
    render() {
        return (
            <button onClick={this.handleClick}>+</button>
        )
    }
}

Context

如果出现层级比较多的情况下(例如:爷爷传递数据给孙子),我们会使用Context来进行传递

  • 作用: 跨组件传递数据
使用步骤
  • 1.调用 React.createContext() 创建 Provider(提供数据) 和 Consumer(消费数据) 两个组件
// 创建context得到两个组件
const { Provider, Consumer } = React.createContext()
  • 2.使用Provider 组件作为父节点
  • 3.设置value属性,表示要传递的数据
 <Provider value="pink">
        <div className="app">
          <Node />
        </div>
      </Provider>
  • 4.哪一层想要接收数据,就用Consumer进行包裹,在里面回调函数中的参数就是传递过来的值
      <Consumer>{data => <span>我是子节点 -- {data}</span>}</Consumer>

总结:

  • 如果两个组件相隔层级比较多,可以使用Context实现组件通讯
  • Context提供了两个组件:Provider 和 Consumer
  • Provider组件: 用来提供数据
  • Consumer组件: 用来消费数据

props进阶

children属性
  • children属性: 表示组件标签的子节点,当组件标签有子节点时,props就会有该属性
  • children属性与普通的props一样,值可以使任意值(文本、react元素、组件、甚至是函数)
  • 1.children为:文本节点
const App = props => {
  console.log(props)
  return (
    <div>
      <h1>组件标签的子节点:</h1>
      {props.children}
    </div>
  )
}
ReactDOM.render(<App>我是子节点</App>, document.getElementById('root')) 
console.png
  • 2.children为:jsx或组件
const Test = () => <button>我是button组件</button>
const App = props => {
  console.log(props)
  return (
    <div>
      <h1>组件标签的子节点:</h1>
      {props.children}
    </div>
  )
}

ReactDOM.render(
  <App>
    <p>我是子节点,是一个p标签</p>
    <Test />
  </App>,
  document.getElementById('root')
)
  • 3.children 属性
const App = props => {
  console.log(props)
  props.children()

  return (
    <div>
      <h1>组件标签的子节点:</h1>
      {/* {props.children} */}
    </div>
  )
}

ReactDOM.render(
  <App>{() => console.log('这是一个函数子节点')}</App>,
  document.getElementById('root')
)
image.png
props校验
  • 对于组件来说,props是外来的,无法保证组件使用者传入什么格式的数据,简单来说就是组件调用者可能不知道组件封装着需要什么样的数据
  • 如果传入的数据不对,可能会导致报错
  • 关键问题:组件的使用者不知道需要传递什么样的数据
  • props校验:允许在创建组件的时候,指定props的类型、格式等


    image.png
  • 1安装包 prop-types (yarn add prop-types | npm i props-types)
  • 2导入prop-types 包
  • 3使用组件名.propTypes={} 来给组件的props添加校验规则
    -4 校验规则通过PropTypes对象来指定
// 1.导入包
import PropTypes from 'prop-types'

const App = props => {
  const arr = props.colors
  const lis = arr.map((item, index) => <li key={index}>{item}</li>)

  return <ul>{lis}</ul>
}

// 2.添加props校验
App.propTypes = {
  colors: PropTypes.array
}

ReactDOM.render(
  <App colors={['red', 'blue']} />,
  document.getElementById('root')
)

常见的约束规则

  • 创建的类型: array、bool、func、number、object、string
  • React元素类型:element
  • 必填项:isRequired
  • 特定结构的对象: `shape({})
    更多的约束规则
    image.png
import PropTypes from 'prop-types'

const App = props => {
  return (
    <div>
      <h1>props校验:</h1>
    </div>
  )
}

// 添加props校验
// 属性 a 的类型:      数值(number)
// 属性 fn 的类型:     函数(func)并且为必填项
// 属性 tag 的类型:    React元素(element)
// 属性 filter 的类型: 对象({area: '上海', price: 1999})
App.propTypes = {
  a: PropTypes.number,
  fn: PropTypes.func.isRequired,
  tag: PropTypes.element,
  filter: PropTypes.shape({
    area: PropTypes.string,
    price: PropTypes.number
  })
}

props的默认值

场景:分页组件 -> 每页显示条数


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

推荐阅读更多精彩内容