React学习笔记(四)--- 组件通讯
作者:互联网
一、组件通讯概念
在React中,组件是独立且封闭的单元,在默认情况下,组件state中的数据是私有数据,组件只能使用自身的数据。但是在实际项目开发过程中,我们的页面是由多个组件共同组成的,而这些组件之间不可避免的要共享某些数据,这个在组件之间传递数据的过程,就称为组件通讯。
二、props 基础
在React中封闭的组件想要接收外部数据应该通过 props 来实现。props是一个只读的对象,对象内的属性值就是外部传递过来的数据,属性只能被读取,无法被修改,属于单向数据流。
组件传递数据的方式就是在使用组件标签时给标签添加属性,属性名和属性值就会传递到props中,props中值的数据类型取决于传递的方式。如果是字符串类型的值,以键值对的形式即可,如果是数值型或其他数据类型就需要用 { }
包裹:
<Hello name="Tom" age={19} />
组件接收数据的方式类组件和函数组件有所不同,在函数组件中,需要把props作为函数的参数,然后通过props.属性名
的形式来访问对应的接收的数据。
// 函数组件
function Hello(props) {
console.log(props)
return (
<div>接收到的name数据:{ props.name }</div>
)
}
而在类组件中,直接以 this.props.属性名
的形式来访问对应的接收的数据。但还有一点要注意:如果在类组件中写了构造函数 constructor ,并且想在构造函数中使用 props ,那就应该将 props 传递给 super() ,否则无法在构造函数中获取使用 props。
// 类组件
class Hello extends React.Component {
constructor(props) {
// 将props传递给父类构造函数
super(props)
}
render() {
return (
<div>接收到的age数据:{ this.props.age }</div>
)
}
}
三、组件通讯的四种方式
1、父向子传递数据
将父组件state中的数据传递给子组件的过程比较简单,只需要父组件在使用子组件标签时添加属性,将数据向子组件传递。然后在子组件中通过 props 来接收相应的数据。
// 父组件传递数据
class Parent extends React.Component {
state = {
name: '朱猪侠'
}
render() {
renturn (
<!-- 通过属性的方式传递数据 -->
<div>传递数据给子组件:<Child name={this.state.name} age={22} /></div>
)
}
}
// 子组件接收数据
function Child(props) {
console.log('子组件的props:', props)
return (
<!-- 通过props获取传递过来的数据 -->
<div>子组件,接收到父组件的数据:姓名:{props.name} 年龄:{props.age}</div>
)
}
子组件的props:
2、子向父传递数据
子组件想要传递数据给父组件,需要利用到回调函数,也就是在父组件中定义一个接收参数的回调函数,然后把这个函数利用属性传递给子组件,子组件通过props调用传递过来的回调函数,并把想要传递的数据作为回调函数的参数,此时父组件中的回调函数就获得了子组件传递过来的数据。
// 父组件
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>
)
}
}
3、同级兄弟组件传递数据
同级的兄弟组件之间如果想要传递数据,则需要先将想要传递的数据提升最近的公共父组件中,让公共父组件管理这个数据,并提供操作该数据的函数。首先要将该数据的操作函数传递到一个子组件A中,然后再将该数据传递到子组件B中。这样当子组件A通过props调用操作函数时,父组件中的数据就会被修改,子组件B中接收的数据也会跟着改变。
简单来说就是:将兄弟组件中想要传递的私有数据提升到公共父组件中,变为共享数据。这样兄弟组件就都可以获得该数据。
// 父组件
class Counter extends React.Component {
// 提供共享状态
state = {
count: 0
}
// 提供修改状态的方法
onIncrement = () => {
this.setState({
count: this.state.count + 1
})
}
render() {
return (
<div>
{/*将数据传递到子组件1中*/}
<Child1 count={this.state.count} />
{/*将数据的操作方法传递到子组件2中*/}
<Child2 onIncrement={this.onIncrement} />
</div>
)
}
}
// 子组件1 展示数据
const Child1 = props => {
return <h1>计数器:{props.count}</h1>
}
// 子组件2 修改数据
const Child2 = props => {
return <button onClick={() => props.onIncrement()}>+1</button>
}
4、Context
上面三种方式已经能够实现大部分场景的数据传递,但如果两个组件之间距离很远,组件嵌套很多,那此时还使用上面的数据传递方法,代码就会变的十分繁琐。所以React还提供了 Context 这一传递数据的方式,来应对跨多个组件传递数据的场景。
Context 提供了两个组件:Provider 和 Consumer 。Provider 组件用来包裹向外传递数据副组件的JSX结构,作为父节点,并通过 value 存储要传递的数据。Consumer 组件用来包裹想要接收使用数据的JSX结构,作为子节点,并通过回调函数的参数来使用存储的数据。
// 1.创建context 并得到Provider 和 Consumer两个组件
const { Provider, Consumer } = React.createContext()
class App extends React.Component {
render() {
return (
// 包裹JSX结构,并通过 value 属性存储想要传递的数据
<Provider value="pink">
<div className="app">
<Node />
</div>
</Provider>
)
}
}
const Node = props => {
return (
<div className="node">
<SubNode />
</div>
)
}
const SubNode = props => {
return (
<div className="subnode">
<Child />
</div>
)
}
const Child = props => {
return (
<div className="child">
{/* 通过函数的参数,使用Provider存储的数据 */}
<Consumer>{data => <span>我是子节点 -- {data}</span>}</Consumer>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
四、props 深入
1、children 属性
在组件中我们可以通过 props 的 children 属性来获取当前组件标签内的子节点,子节点可以是文本、React元素、组件以及函数等。
// children 为 文本节点、组件JSX
const App = props => {
console.log(props)
return (
<div>
<h1>组件标签的子节点:</h1>
{props.children}
</div>
)
}
const Test = () => <button>我是button组件</button>
ReactDOM.render(
<App>
{/* <p>我是子节点,是一个p标签</p> */}
{/* <Test /> */}
我是子节点
</App>, document.getElementById('root'))
// 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')
)
2、校验 props 的数据类型
对于组件本身来说,props 是由使用者传递过来的,数据类型和格式由使用者决定。但在实际开发中,我们通常只希望使用者传递我们所规定的数据类型。所以react提供了一个名为 prop-types
的依赖包,可以让我们指定传递给组件的数据格式,而且如果传递的数据不符合指定的数据格式,还会在控制台给出明确的错误提示。
在项目中使用 prop-types 的步骤:
① 安装依赖包 npm install props-types 或 yarn add prop-types
② 导入下载的依赖包
③ 通过 组件名.proptypes = { prop名称: PropTypes.数据类型 }
来给组件的props指定数据类型
// 导入包
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>
}
// 添加props校验
App.propTypes = {
colors: PropTypes.array
}
ReactDOM.render(
<App colors={['red', 'blue']} />,
document.getElementById('root')
)
常见的prop约束规则:
① 常见数据类型:array、bool(即boolean)、func(即function)、number、object、string 等
② React元素类型:element
③ 必填验证:isRequired,即要求props中必须有该数据。
④ 特定结构的对象:shape({ }) ,即对 对象中的 某些属性的数据类型,做出约束。
注意: 当想要同时使用多条验证规则时,只需要采用链式编程的方式即可,如 PropTypes.func.isRequired
,要求既是函数又是必填项。
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
})
}
ReactDOM.render(<App fn={() => {}} />, document.getElementById('root'))
3、prop 默认值
我们可以给组件的某些prop,设置默认值,当组件在使用时,没有传入该prop数据,那我么那就使用默认值来设置组件。设置默认值需要借助于组件的 defaultProps 属性:
const App = props => {
return (
<div>
<h1>props校验:{ props.a }</h1>
</div>
)
}
App.defaultProps = {
a: 123 // 给props中的 a 设置默认值
}
ReactDOM.render(<App />, document.getElementById('root'))
标签:React,return,---,传递,props,组件,数据,传递数据 来源: https://blog.csdn.net/weixin_45092437/article/details/122631599