组件通信的意义
组件之间的数据流转
组件通信
- 父子关系——最重要
- 兄弟关系——自定义事件模式,eventBus/通过共同的父组件通信
- 其他关系——vue中使用vuex,react使用mobx/redux/基于hook的方案
父传子实现
实现父子通信中的父传子,把父组件中的数据传给子组件
实现步骤
-
父组件提供要传递的数据 -
state
-
给子组件标签
添加属性
值为 state中的数据 -
子组件中通过
props
接收父组件中传过来的数据 -
- 类组件使用this.props获取props对象
- 函数式组件直接通过参数获取props对象
import React, { createRef } from "react"
// APP是父组件,
//Son1是子组件(函数组件)
//Son2是子组件(类组件)
function Son1 (props) {
return (
<div>
<h2>{props.msg}</h2>
我是函数组件
</div>
)
}
class Son2 extends React.Component {
state = {
son2Msg: this.props.msg
}
render () {
return (
<>
<h1>{this.state.son2Msg}</h1>
我是类组件
</>
)
}
}
class App extends React.Component {
state = {
msg: "this is app msg"
}
render () {
return (
<div className="App">
<Son1 msg={this.state.msg} />
<Son2 msg={this.state.msg} />
</div>
)
}
}
export default App
props说明
和vue一样,props是只读,不能做修改
props可以传递任意数据,数字,字符串,布尔值,数组,对象,函数,JSX
function Son1 (props) {
return (
<div>
<h2>{props.msg}</h2>
<h2>{props.list.map(item => <p key={item}>{item}</p>)}</h2>
<h3>{props.userInfo.name}</h3>
<button onClick={props.cb}>触发父组件传入的函数</button>
{props.jsxd}
我是函数组件
</div>
)
}
class App extends React.Component {
state = {
msg: "this is app msg",
list: [1, 2, 3],
userInfo: {
name: 'seek',
age: 18
}
}
render () {
return (
<div className="App">
<Son1
msg={this.state.msg}
list={this.state.list}
userInfo={this.state.userInfo}
cb={() => { console.log(123) }}
jsxd={(<div><h1>asdfs</h1></div>)}
/>
</div>
)
}
}
export default App
父传子传递函数是子传父的实现
父传子jsx类似于vue的插槽
props赋值方法
function Son1 (props) {
const { msg, list, userInfo, db, jsxd } = props
}
function Son1 ({ msg, list, userInfo, db, jsxd }) {
}
这里写的都是原生的js函数,原生js支持的方法,react都是可以的
实际工程中会使用第二种
子传父
通过方式:父传给子一个函数,通过函数来实现子传父
import React, { createRef } from "react"
function Son1 (props) {
const { getSonMsg } = props
return (
<div>
我是函数组件
<button onClick={() => getSonMsg(123)}>点击按钮传入父组件</button>
</div>
)
}
class App extends React.Component {
state = {
msg: "this is app msg",
list: [1, 2, 3],
userInfo: {
name: 'seek',
age: 18
}
}
getSonMsg = (msg) => {
this.setState({
msg: msg
})
}
render () {
return (
<div className="App">
<h1>{this.state.msg}</h1>
<Son1 getSonMsg={this.getSonMsg} />
</div>
)
}
}
export default App
为了减少写在jsx上过多,可以将函数抽到外面
function Son12 (props) {
const { getSonMsg } = props
function clickHandler () {
getSonMsg(123)
}
return (
<div>
我是函数组件
<button onClick={clickHandler}>点击按钮传入父组件</button>
</div>
)
}
兄弟组件通信
借助共同的父组件,实现兄弟间通信
import React, { createRef } from "react"
// 目标:B组件的数据传入A
// 方案:
// 1,先把B中的数据通过子传父,传给APP
// 2,再把App接收到的Son中的数据,通过父传子,传给A、
function SonA (props) {
return (
<div>
this is A
<h2>{props.msg}</h2>
</div>
)
}
function SonB (props) {
function clickHandler () {
props.getSonMsg("我是b的msg")
}
return (
<div>
<h1>this is B</h1>
<button onClick={clickHandler}>点击传递给组件A</button>
</div>
)
}
class App extends React.Component {
state = {
msg: "this is app msg",
}
getSonMsg = (msg) => {
this.setState({
msg: msg
})
}
render () {
return (
<div className="App">
<h1>APP:{this.state.msg}</h1>
<SonA msg={this.state.msg} />
<SonB getSonMsg={this.getSonMsg} />
</div>
)
}
}
export default App
跨组件通信
了解Context机制解决
如上:通过APP传递给Comc,可以通过父传子,但是层数过多,很麻烦
vue中使用provide inject
react使用context
import React, { createContext, createRef } from "react"
// APP->A->B->C
// 目标:APP->C
// 1,导入createContext方法并且执行.获取provider,consumer
// 2,使用provider包裹
// 3,使用consumer包裹
const context = createContext()
const { Provider, Consumer } = context
function SonA () {
return (
<>
<h3>this is SonA</h3>
<SonB></SonB>
</>
)
}
function SonB () {
return (
<>
<h3>this is SonB</h3>
<SonC></SonC>
</>
)
}
function SonC () {
return (
<>
<h3>this is SonC</h3>
<Consumer>
{value => <span>{value}</span>}
</Consumer>
</>
)
}
class App extends React.Component {
state = {
msg: "this is app msg",
}
getSonMsg = (msg) => {
this.setState({
msg: msg
})
}
render () {
return (
<Provider value={this.state.msg}>
<div className="App">
<SonA />
</div>
</Provider>
)
}
}
export default App
props属性
在组件内部写了,就会自动出现在children属性
children表示该组件的子节点,只要组件内部有节点,props就有该属性
children:
- 普通文本
- 普通标签元素
- 函数
- JSX
目的:高阶组件
<SonA>this is child</SonA>
如下:类似于vue的插槽
function SonA ({ children }) {
return (
<>
<h3>this is SonA</h3>
<span>{children}</span>
</>
)
}
class App extends React.Component {
render () {
return (
<div className="App">
<SonA><h2>this is span child</h2></SonA>
</div>
)
}
}
传递函数
function SonA ({ children }) {
children()
return (
<>
<h3>this is SonA</h3>
</>
)
}
class App extends React.Component {
render () {
return (
<div className="App">
<SonA>{() => console.log(123)}</SonA>
</div>
)
}
}
传递JSX
function SonA ({ children }) {
return (
<>
<h3>this is SonA</h3>
{children}
</>
)
}
class App extends React.Component {
render () {
return (
<div className="App">
<SonA>{<div><p>{'this is p'}</p></div>}</SonA>
</div>
)
}
}
传递多个标签
function SonA ({ children }) {
return (
<>
<h3>this is SonA</h3>
{children.map(child => child)}
</>
)
}
class App extends React.Component {
render () {
return (
<div className="App">
<SonA>
<div><p>{'this is p'}</p></div>
<p>{'this is p'}</p>
</SonA>
</div>
)
}
}
props校验
安装属性校验包
npm install prop-types
导入prop-types包
使用组件名.propTypes={} 给组件添加校验规则
第一个会报错,因为不是数组
import React, { createContext, createRef } from "react"
// 里面有各种各样的内置校验规则
import PropTypes from 'prop-types'
const TestPropTypes = ({ list }) => {
return (
<>
<div>this is test</div>
{list.map(item => <h2 key={item}>{item}</h2>)}
</>
)
}
TestPropTypes.prototypes = {
list: PropTypes.array
}
class App extends React.Component {
state = {
list: [1, 2, 3]
}
render () {
return (
<div className="App">
<TestPropTypes list={2} />
<TestPropTypes list={this.state.list} />
</div>
)
}
}
export default App
四种常见结构
-
常见类型:array、bool、func、number、object、string
-
React元素类型:element
-
必填项:isRequired
-
特定的结构对象:shape({})
官方文档:
https://reactjs.org/docs/typechecking-with-proptypes.html
prop默认值
函数默认值
方法一:(调试工具能看到)
使用prop-types包,使用defaultProps定义默认值
如果父元素没有传,则使用默认值
TestPropTypes.defaultProps = {
list: [2, 3, 4]
}
方法二:(调用工具看不到,但是数据是可用的),推荐方案
const TestPropTypes = ({ list = [2, 3, 7] }) => {
return (
<>
<div>this is test</div>
{list.map(item => <h2 key={item}>{item}</h2>)}
</>
)
}
类组件默认值
方案一:使用prop-types插件来实现
class TestClass extends React.Component {
render () {
return (
<>
<div>this is class component</div>
<h2>{this.props.number}</h2>
</>
)
}
}
TestClass.defaultProps = {
number: 10
}
方法二:使用static(推荐)
class TestClass extends React.Component {
static defaultProps = {
number: 20
}
render () {
return (
<>
<div>this is class component</div>
<h2>{this.props.number}</h2>
</>
)
}
}
如果两个同时使用,prop-types定义为准