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