10. React组件间的通信
3. React组件间的通信
-
3.1. 认识组件嵌套
-
1. 组件之间存在嵌套关系:
- 1.1. 在之前的案例中,只是创建了一个组件App;
- 1.2. 如果我们一个应用程序将所有的逻辑都放在一个组件中, 那么这个组件就会变得非常的臃肿和难以维护;
- 1.3. 所以组件化的核心思想应该是对组件进行拆分,拆分成一个个小的组件;
- 1.4. 在将这些组件组合嵌套在一起,最终形成应用程序;
-
2. 上面的嵌套逻辑如下,它们存在如下关系:
- 2.1. App组件是Header、Main、Footer组件的父组件;
- 2.2. Main组件是Banner、ProductList组件的父组件
- 2.3. 如下图
-
3. 提高开发效率的vscode插件
-
- 原因:每次开发时都需要引入组件,都需要引入相关的依赖,很麻烦。
-
- 使用插件:
ES7+ React/Redux/React-Native snippets
, 这个插件会包含很多的代码片段还挺好用的,所以使用了这个插件的代码片段,
- 使用插件:
-
-
3.2. 认识组件嵌套
-
3.2.1 在开发过程中,我们会经常遇到需要组件之间相互通信:
-
- App可能使用了多个Header, 每个地方的Header展示的内容不同,那么我们就需要使用者传递给Header一些数据,让其进行展示
-
- 又比如在Main中一次性请求了Banner数据和ProductList数据,那么就需要传递给他们来进行展示;
-
- 也可能是子组件中发生了事件,需要又父组件来完成某些操作,那就需要子组件向父组件传递事件;
-
-
3.2.2 总之,在一个React项目中,组件之间通信是非常重要的环节;
-
3.2.3 父组件在展示子组件,可能会传递一些数据给子组件;
-
- 父组件通过
属性=值
的形式来传递给子组件数据;
- 父组件通过
-
- 父组件通过
props参数获取父组件传递过来的数据
;
- 父组件通过
-
- 示例代码如下:
- 父组件:
import React, { Component } from 'react'import MainBanner from './MainBanner'import MainProductList from './MainProductList'export class Main extends Component {constructor () {super()this.state = {title: '商品轮播图',banners: ['新歌单','新MV', '新歌曲'],productList: ['推荐商品', '热门商品', '流行商品']}}render() {const { title, banners, productList} = this.statereturn (<div className='main'><div>main</div><MainBanner banners={banners} title={title} /><MainProductList productList={productList} /></div>)}}export default Main
- 子组件
import React, { Component } from 'react'export class MainBanner extends Component {constructor(props) {// 把props传递给super,内部会自动保存执行这个操作this.props = props// 如果不写constructor,默认就会执行super(props);自动保存this.props = propssuper(props)this.state = {name: 'MainBanner'}}render() {const { title, banners} = this.propsconsole.log('props==', this.props)return (<div><h2>{title}</h2><ul>{banners.map(item => {return (<li key={item}>{item}</li>)})}</ul></div>)} }export default MainBanner
-
-
-
3.3. 参数propsTypes
-
3.1. 对于传递给子组件的数据,有时候我们可能希望进行验证,特别是大型项目来说:
-
- 当然,如果你项目中默认
继承了Flow或者TypeScript
,那么直接就可以进行类型验证
;
- 当然,如果你项目中默认
-
- 但是,即使我们没有使用Flow或者TypeScript,也可以
通过props-types库来进行参数验证
;
- 但是,即使我们没有使用Flow或者TypeScript,也可以
-
-
3.2. 从React v15.5开始,React.PropTypes已移入另一个包中:prop-types库;
-
3.3. 更多的验证方式,可以参考官网:官网地址
-
1.比如验证数组,并且数组中包含哪些元素;
-
2.比如验证对象,并且对象中
包含哪些key以及value是什么类型
; -
3.比如某个原生是必须的,使用
requiredFunc: PropTypes.func.isRequired
;- 设置必选和指定类型
import React, { Component } from 'react'import PropTypes from 'prop-types';export class MainBanner extends Component {constructor(props) {// 把props传递给super,内部会自动保存执行这个操作this.props = props// 如果不写constructor,默认就会执行super(props);自动保存this.props = propssuper(props)this.state = {name: 'MainBanner'}}render() {const { title, banners} = this.propsreturn (<div><h2>{title}</h2><ul>{banners.map((item) => {return (<li key={item.acm}>{item.title}</li>)})}</ul></div>)}}// MainBanner.propTypes = {// banners: PropTypes.array.isRequired,// title: PropTypes.string.isRequired,// };MainBanner.propTypes = {banners: PropTypes.array.isRequired,title: PropTypes.string.isRequired};export default MainBanner
- 不传值或者传值类型为其他类型,会报错
import React, { Component } from 'react'// import MainBanner from './MainBanner'import MyComponent from './MainBanner'import MainProductList from './MainProductList'import axios from 'axios'export class Main extends Component {constructor () {super()this.state = {title: '商品轮播图',banners: [],productList: []}}componentDidMount () {axios.get('http://123.207.32.32:8000/home/multidata').then(res => {console.log(res);const data = res.data.dataconst banners = data.banner.listconst recommend = data.recommend.listthis.setState({banners,productList: recommend})})}render() {const { title, banners, productList} = this.state// return (<div className='main'><div>main</div><MyComponent banners={banners} title={title}/><MyComponent /><MainProductList productList={productList} /></div>)}}export default Main
- 报错图如下:
- 正确传参:
import React, { Component } from 'react' // import MainBanner from './MainBanner' import MyComponent from './MainBanner' import MainProductList from './MainProductList' import axios from 'axios'export class Main extends Component {constructor () {super()this.state = {title: '商品轮播图',banners: [],productList: []}}componentDidMount () {axios.get('http://123.207.32.32:8000/home/multidata').then(res => {console.log(res);const data = res.data.dataconst banners = data.banner.listconst recommend = data.recommend.listthis.setState({banners,productList: recommend})})}render() {const { title, banners, productList} = this.state// return (<div className='main'><div>main</div><MyComponent banners={banners} title={title}/><MyComponent banners={banners} title={title}/><MainProductList productList={productList} /></div>)} }export default Main
-
-
3.4. 如果没有传递的话,希望有默认值;
-
- 使用
defaultProps
就可以了
- 使用
-
-
-
3.4. 子组件传递父组件
-
3.4.1. 某些情况,我们也需要子组件向父组件传递消息:
-
- 在Vue中是通过自定义事件来完成的;
-
- 在React中同样是
通过props传递消息
,只是让父组件给子组件传递一个回调函数
,在子组件中调用这个函数即可
;
- 在React中同样是
-
-
3.4.2. 这里完成一个案例
-
- 将计数器案例进行拆解
-
- 将按钮封装到子组件中:CounterButton;
-
- CounterButton发生点击事件,将内容传递给父组件,修改counter的值;
-
-
3.4.3. 示例代码如下:
- 父组件:App.jsx
import React, { Component } from 'react'import AddCount from './AddCounter'import SubCount from './SubCounterr'export class App extends Component {constructor() {super()this.state = {counter: 0}}changeCounter (counter) {this.setState({counter: this.state.counter + counter})}render() {const { counter } = this.statereturn (<div><h2>当前计数:{counter}</h2><AddCount addClick={counter => this.changeCounter(counter) } /><SubCount subClick={counter => this.changeCounter(counter) } /></div>)}}export default App
- 子组件:AddCounter.jsx
import React, { Component } from 'react'export class AddCount extends Component {addCount(count) {// 拿到父组件传递过来的函数,把参数传递到函数里面,相当于把子组件的传递给父组件传参this.props.addClick(count)}render() {return (<div><button onClick={e => this.addCount(1)}>+1</button><button onClick={e => this.addCount(5)}>+5</button><button onClick={e => this.addCount(10)}>+10</button></div>)}}export default AddCount
- 子组件:SubCounter.j
import React, { Component } from 'react'export class subCount extends Component {subCount (count) {this.props.subClick(count)}render() {return (<div><button onClick={e => this.subCount(-1)}>-1</button><button onClick={e => this.subCount(-5)}>-5</button><button onClick={e => this.subCount(-10)}>-10</button></div>)}}export default subCount
-
-
3.5. 子组件传父组件案例练习
-
1.如下图
-
- 父组件App代码:
import React, { Component } from 'react'import TabControl from './TabControl'export class App extends Component {constructor() {super()this.state = {titles: ['流行', '新款', '热门'],tabIndex: 0}}tabClick (index) {this.setState({tabIndex: index})}render() {const { titles,tabIndex } = this.statereturn (<div className='app'><TabControl titles={ titles } tabClick={i => this.tabClick(i)}/><h2>{titles[tabIndex]}</h2></div>)}}export default App ```·
-
- 子组件TabControl代码
import React, { Component } from 'react'import './style.css'export class TabControl extends Component {constructor() {super()this.state = {currentIndex: 0}}changeTab (index) {this.props.tabClick(index)this.setState({currentIndex: index})} render() {const { titles } = this.propsconst { currentIndex } = this.statereturn (<div><div className="tab-control">{titles.map((item, index) => {return (<div className={`item ${currentIndex === index ? 'active' : ''}`} key={index} onClick={(e) => this.changeTab(index)}><span className='text'>{item}</span></div>)})}</div></div>)}}export default TabControl
-