React--》React组件间数据的通信与共享

目录

组件通信

props的特点

props深入

父向子传递数据

子向父传递数据

兄弟组件传递数据

消息订阅与发布

Context实现跨组件传递数据


组件通信

组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据。在组件化过程中,我们将一个完整的功能拆分成多个组件,以更好的完成整个应用的功能。而在这个过程中,多个组件之间不可避免的要共享某些数据。为了实现这些功能,就需要打破组件的独立封闭性,让其与外界沟通。这个过程就是组件通讯

组件是封闭的,要接收外部数据应该通过 props 来实现props的作用:接收传递给组件的数据。

传递数据:给组件标签添加属性。

<App name="jack" age={12} />

接收数据函数组件通过参数props接收数据,类组件通过 this.props 接收数据。

// 函数组件
function App(props){
  return <div>
    接收的数据:
    姓名:{props.name}
    年龄:{props.age}
  </div>
}
// 类组件
class App extends React.Component {
  render(){
    return <div>
      接收到的数据:
      姓名:{this.props.name}
      年龄:{this.props.age}
    </div>
  }
}

props的特点

props是可以传递任意类型的数据的

import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
  render(){
    return <div>
      接收到的数据:
      姓名:{this.props.name}
      年龄:{this.props.age}
      颜色:{this.props.color}
      函数:{this.props.fn()}
      标签:{this.props.tag}
    </div>
  }
}
ReactDOM.render(<App
   name="jack"
   age={12}
   color={['red','green','blue']}
   fn={()=>console.log('这是一个函数')}
   tag={<p>这是一个p标签</p>}
   />,
document.getElementById('root'))

props是只读的不能进行修改

在使用类组件时,如果写了构造函数,应将props传递给super(),否则无法在构造函数中获取到props:

import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
  // 推荐使用props作为constructor的参数!
  constructor(props){
    super(props)
    console.log(props)
  }
  render(){
    return <div>
      接收到的数据:
      姓名:{this.props.name}
    </div>
  }
}
ReactDOM.render(<App name="jack" />,document.getElementById('root'))

props深入

children属性:表示组件标签的子节点。当组件标签有子节点时,props就会有该属性。children属性与普通的props一样,值可以是任意值(文本、React元素、组件、甚至是函数)。

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  render(){
    return (
      <div>
        <h1>当前组件的子节点:</h1>
        {this.props.children}
      </div>
    )
  }
}

ReactDOM.render(<App>
  <p>我是子节点</p>
</App>,document.getElementById('root'))

props校验: 对于组件来说,props是外来的,无法保证组件使用者传入什么格式的数据,如果传入的数据格式不对,可能会导致组件内部报错。此时需要props校验:允许在创建组件的时候,就指定props的类型,格式等。其使用步骤如下:

终端安装 props-types 包:

npm install prop-types --force

导入并使用该包,来给组件的 props 添加校验规则:

import React from 'react';
import ReactDOM from 'react-dom';

// 导入包
import PropTypes from 'prop-types'
const App = props =>{
  const arr = props.hobby
  const list = arr.map((item,index)=><li key={index}>{item}</li>)
  return <ul>{list}</ul>
}
// 添加props校验
App.propTypes = {
  // 约定hobby属性为array类型,如果类型不对,则报出明确错误,便于分析错误原因!
  hobby: PropTypes.array
}
ReactDOM.render(<App hobby={['抽烟','喝酒','烫头']} />,document.getElementById('root'))

props校验常见的约束规则

常见类型:array、bool、func、number、object、string

React元素类型:element

必填项:isRequired

特定结构的对象:shape( { } )

还有许多的约束规则请参考React官方文档:使用 PropTypes 进行类型检查

props的默认值:给props设置默认值,在未传入 props 时生效。

import React from 'react';
import ReactDOM from 'react-dom';

function App(props) {
  return (
    <div>
      props的默认值:{props.pageSize}
    </div>
  )
}
// 设置App的默认值
App.defaultProps = {
  pageSize: 10
}
ReactDOM.render(<App />,document.getElementById('root'))

父向子传递数据

父组件要想向子组件传递数据需提供state数据;给子组件标签添加属性值为state中的数据;子组件中通过props接收父组件中传递的数据

import React from 'react';
import ReactDOM from 'react-dom';
// 父组件(类组件)
class Father extends React.Component {
  state = {
    msg:'你能看到我吗?子组件'
  }
  render(){
    return <div>
      <Son getmsg={this.state.msg} />
    </div>
  }
}
// 子组件(函数组件)
const Son = (props)=>{
  return <div>
    父组件你说:{props.getmsg}
    <br></br>
    子组件:当然拿到了
  </div>
}
ReactDOM.render(<Father />,document.getElementById('root'))

子向父传递数据

利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数。父组件提供一个回调函数(用于接收数据),将该函数作为属性的值,传递给子组件

import React from 'react';
import ReactDOM from 'react-dom';
// 父组件(类组件)
class Father extends React.Component {
  // 提供回调函数,用来接收数据
  getChildMsg = (data)=>{
    console.log('接收到子组件中传递过来的数据:',data)
  }
  render(){
    return <div>
      <Son getMsg={this.getChildMsg} />
    </div>
  }
}

// 子组件(类组件)
class Son extends React.Component {
  // 子组件的状态数据
  state = {
    msg:'父组件,你能看到我吗?'
  }

  handleClick = ()=>{
    // 子组件调用父组件中传递过来的回调函数
    this.props.getMsg(this.state.msg)
  }
  render(){
    return <button onClick={this.handleClick}>点我,向父组件传递数据</button>
  }
}
ReactDOM.render(<Father />,document.getElementById('root'))

兄弟组件传递数据

共享的状态提升到最近的公共父组件中,由公共父组件管理这个状态。公共父组件职责是提供共享状态提供操作共享状态的方法;要通讯的子组件只需通过props接收状态或操作状态的方法。

import React from 'react';
import ReactDOM from 'react-dom';
// 父组件(类组件)
class Father extends React.Component {
  // 父组件的状态数据
  state = {
    count:0
  }
  // 提供修改状态的方法
  onIncrement = ()=>{
    this.setState({
      count:this.state.count+1
    })
  }
  
  render(){
    return <div>
      <Son1 count={this.state.count} />
      <Son2 onIncrement={this.onIncrement}/>
    </div>
  }
}

// 子组件1
const Son1 = props =>{
  return <h1>计数器:{props.count}</h1>
}

// 子组件
const Son2 = props =>{
  return <button onClick={()=> props.onIncrement()}>+1</button>
}
ReactDOM.render(<Father />,document.getElementById('root'))

消息订阅与发布

如果觉得上文兄弟组件共享数据,需要借助同个父组件做中间人才能共享数据太过繁琐,可以采用这种消息订阅与发布的方式,其文档介绍网址:pubsub技术 。先安装这个包才能使用!其安装命令为:npm i prop-types --force 。

兄弟组件right,消息的发送方:

import React from 'react';
import PubSub from 'pubsub-js'

export default class Right extends React.Component {
  handlePub = ()=>{
    // 参数1是:传递名称,参数2是:传递数据
    PubSub.publish('xxx',{orignMsg:'你的数据被修改了'})
  }
  render(){
    return (
      <div>
        <h1>我是右组件,我也是消息发送方</h1>
        <button onClick={this.handlePub}>点我发送消息</button>
      </div>
    )
  }
}

兄弟组件left,消息的接收方:

import React from 'react';
import ReactDOM from 'react-dom';
import PubSub from 'pubsub-js'

export default class Left extends React.Component {
  state = {
    orignMsg:'这是我原来的数据'
  }
  componentDidMount(){
    this.token = PubSub.subscribe('xxx',(_,stateObj)=>{
      this.setState(stateObj)
    })
  }
  // 只要组件即将被卸载掉就取消订阅
  componentWillUnmount(){
    console.log('组件卸载,取消订阅')
    PubSub.unsubscribe(this.token)
  }
  death = ()=>{
    ReactDOM.unmountComponentAtNode(document.getElementById('root'))
  }
  render(){
    return (
      <div>
        <h1>我是左组件,是消息的接收方</h1>
        <h2>{this.state.orignMsg}</h2>
        <button onClick={this.death}>点击卸载组件</button>
      </div>
    )
  }
}

将两个兄弟组件在父组件中展示:

import React from 'react';
import ReactDOM from 'react-dom';
import Right from  './components/right'
import Left from  './components/left'

class App extends React.Component {
  render(){
    return (
      <div>
        <Right />
        <Left />
      </div>
    )
  }
}
ReactDOM.render(<App />,document.getElementById('root'))

Context实现跨组件传递数据

如果两个组件是多层嵌套,可以使用Context实现组件通讯。Context提供了两个组件:Provider组件(用来提供数据),Consumer组件(用来消费数据)。

import React from 'react';
import ReactDOM from 'react-dom';

// 调用React.createContext创建Provider(提供数据)和Consumer(消费数据)两个组件。
const {Provider,Consumer} = React.createContext()
// 父组件(类组件)
class Father extends React.Component {
  state = {
    msg:'你能看到我吗'
  }
  render(){
    // 使用Provider作为父节点,设置value属性,表示要传递的数据
    return  <Provider value={this.state.msg}>
      <div>
        <h1>我是父节点,我要传递的数据是:{this.state.msg}</h1>
        <A />
      </div>
    </Provider>
  }
}

// 组件A
const A = props =>{
  return <div>
    <B />
  </div>
}
// 组件B
const B = props =>{
  return <div>
    <C />
  </div>
}
// 组件C
const C = props =>{
  return <div>
    <Son />
  </div>
}
class Son extends React.Component {
  render(){
    return <div>
      {/* 使用Consumer作为子节点,通过箭头函数来获取父组件传递过来的值 */}
      <Consumer>
        {
          data => <span>我是子节点 -- {data}</span>
        }
      </Consumer>
    </div>
  }
}
ReactDOM.render(<Father />,document.getElementById('root'))

猜你喜欢

转载自blog.csdn.net/qq_53123067/article/details/127081415