zoukankan      html  css  js  c++  java
  • react总结之组件通信(props,context)

      react其组件化的思想使得组件与组件间的通信变的十分必要,下要我们来分别介绍常见的react组件之间的通信方式。

      一、父子组件

      react是单向数据流,父组件在展示子组件时,可能会传递一些数据给到子组件。父组件通过过属性=值的形式传递给子组件数据,子组件通过props参数获取父组件传递过来的数据。而在某些情况下我们也需要子组件向父组件传递消息,这时同样需要用到props,父组件通过props给子组件传递一个自定义的回调函数,随后在子组件中进行接收和调用,这样子组件的操作就影响了父组件中的数据 。

    import React from 'react';
    import ReactDOM from 'react-dom';
    class SonClassComponent extends React.Component {
        render() {
            const { name, age } = this.props;
            const { btnClick } = this.props;
            return (
                <div>
                    {/* 父传子:子组件通过props参数来获取父组件传递的数据 */}
                    <h4>父传子:my name is {name},{age} years old</h4>
                    {/* 子传父:子组件中调用这个函数 */}
                    <button onClick={btnClick}>+ click </button>
                </div>
            )
        }
    }
    function SonFunComponent(props) {
        return <h4> 父传子:my favorite book is {props.book} </h4>      
    }
    class Father extends React.Component {
        state = { name: "davina", book: '《The Dream of Red Mansion》', counter: 0 }
        render() {
            const { name, book, counter } = this.state;
            return (<>
                <h3>当前计数是:{counter}</h3>
                {/* 这个地方须要注意this指向的问题 */}
                <button onClick={this.addCounter}>+</button>
                {/* 父传子:父组件通过属性=值传递数据 */}
                {/* 子传父:还是通过props,父组件给子组件传递一个回调函数(自定义) */}
                <SonClassComponent name={name} age={18} btnClick={this.addCounter} />
                <SonFunComponent book={book}  />
            </>)
        }
        addCounter = () => {
            this.setState({ counter: this.state.counter + 1 })
        }
    }
    ReactDOM.render(< Father />, document.getElementById('root'))

      二、属性验证(propTypes)

      对于传递的数据有时我们需要进行验证,看是否符合相应的要求,一般它是用在复用性较强的组件上。我们使用propType库进行验证时,首先要导入prop-types,其次是 配置特定的propTypes属性。如果有某个属性是必须传递的,我们可以用propTypes.xxx.isRequired,或者是没有传递props但希望有一个默认值,可以使用类名.defaultProps={}或者是用到静态属性也是可以的。

    //1.导入
    import propTypes from 'prop-types'
    import React from 'react'
    import ReactDom from 'react-dom'
    class Son extends React.Component {
        // 类中使用static propTypes = {} 进行检证
        static propTypes = {
            data: propTypes.string,  //data必须是一个字符串  
            className:propTypes.string.isRequired //设置必传属性的值
        }
        // 设置默认值方法一:
        static defaultProps = {data: '暂无'}
        render() {
            let {className,data} = this.props
            return <div className={className}>{data}</div>
        }
    }
    // 设置默认值方法二
    // Son.defaultProps = {data: '暂无'}
    class Father extends React.Component {
        state = {
            data: '杨柳回塘,鸳鸯别浦,绿萍涨断莲舟路',
            className: 'box'
        }
        render() {
            let { data, className } = this.state
            return (
                <Son className={className} data={data}></Son>
            )
        }
    }
    ReactDom.render(<Father />, document.getElementById('root'))

      三、context

      对于非父子层级较深的组件进行通信时我们可以使用react提供的一个API即Context,它提供了一种在组件之间共享数据的方式,而不必通过每一层组件,可以共享一些数据,其它的组件都可以从中进行读取。有新旧两种写法。

      context它有四个与之相关的api需要我们了解,即:

        React.createContext:使用它可以创建一个需要共享的Context对象,如果一个组件订阅了Context,那么这个组件就会从离自身最近的那个匹配的Provider中读取到当前context的值,如果没有找到对应的Provider可以设置一个defaultValue。

        Context.Provider:每个Context对象都会返回一个Provider React组件,它允许消费组件订阅context的变化。Provider接收一个value值,传递给消费组件,当Provider的value值发生变化,它内部的所有消费组件都会被重新的渲染。并且一个Provider可以和多个消费组件进行对应,多个Provider也可嵌套使用,里层数据会覆盖外层数据。

        Class.contextType:是挂载在class上的contextType属性它会被重新赋值一个由React.createContext()创建的Context对象,这样我们就可以使用this.context来得到使用最近Context上的值;

        Context.Consumer:使用这个api,react组件可以订阅到context的变化,这样我们可以在函数式组件中完成订阅context的任务了,强调一点的是它需要函数作为子元素,这个函数接收当前context值,返回一个react节点。

      老写法:声明上下文前首先我们要用到static chiildContextTypes 声明上下文中的数据类型,其次是getChildrenContext方法将属性传递给子组件,在子组件中需要使用contextTypes声明需要用到的属性的数据类型。

    //引入
    import propTypes from 'prop-types'
    import React from 'react'
    import ReactDom from 'react-dom'
    class Son extends React.Component {
        //3、接收
        static contextTypes ={data:propTypes.string}
        render() {
            //4、使用
            return <div>{this.context.data}</div>
        }
    }
    class Father extends React.Component {
        //1、声明
        static childContextTypes = {
            data: propTypes.string,
            name:propTypes.string
        }
        //2、传递
        getChildContext() {
            return {data: '杨柳回塘,鸳鸯别浦,绿萍涨断莲舟路'}
        }
        render() {
            return <div className=''><Son></Son></div>
        }
    }
    ReactDom.render(<Father />, document.getElementById('root'))

      新写法:首先用到React.createContext创建一个全局的共享对象数据,然后使用Context对象的Provider react组件得到数据,Context.Provider它可以让每个组件订阅context的变化 ,如果一个组件订阅了Context,那么这个组件就会从离自己最近的组件匹配provider进行读取操作,这样可以使用数据了。

    import React from 'react'
    import ReactDom from 'react-dom'
    // 1、创建
    let Context = React.createContext();
    class Son extends React.Component {
        //3.重新赋值挂载
       static contextType = Context;
        render() {
           //4.用this.context来使用最近Context上的那个值
            return <div>{this.context.data}</div>
        }
    }
    class Father extends React.Component {
        render() {
            return <div className=''><Son></Son></div>
        }
    }
    ReactDom.render(
        //2.使用Context.Provider,传递数据
        <Context.Provider value={{data:'杨柳回塘,鸳鸯别浦,绿萍涨断莲舟路'}}>
            <Father />
        </Context.Provider>, document.getElementById('root'))

      四、react中插槽实现

       在react中是没有slot这个概念的,如果要实现vue中slot效果,可以直接通过props进行传输。

       下面的NavChildren组件中就用到props.children属性。这个属性它在每个组件中都有,包含了组件开始和结束标记之间的内容,可以插入文本,表达式或者是节点。this.props.children的值有三种情况,如果当前组件没有子节点,那它就为undefined,如果有一个子节点,那数据类型为object,如果多个子节点,数据类型为array。当传入多个子节点,props.children就是一个数组,那就可以通过其下标访问到子节点,用以控制其出现的位置。但它对顺序有极高的要求,又因为子父组件之间的传递及组件可以接受任意的props,所以我们可以像NavBar那样直接进行使用。

    // demo.js
    import React, { PureComponent } from 'react';
    import ReactDOM from 'react-dom';
    import './style.css'
    class NavChildren extends PureComponent {
        render() {
            // this.props.children,用来children,但是它对顺序有极高的要求
            return (
                <div className="nav-item nav-bar">
                    <div className="nav-left">{this.props.children[0]}</div>
                    <div className="nav-item nav-center">{this.props.children[1]}</div>
                    <div className="nav-item nav-right">{this.props.children[2]}</div>
                </div>
            )
        }
    }
    class NavBar extends PureComponent {
        render() {
            return (
                <div className="nav-item nav-bar">
                    <div className="nav-left">{this.props.slotLeft}</div>
                    <div className="nav-item nav-center">{this.props.slotCenter}</div>
                    <div className="nav-item nav-right">{this.props.slotRight}</div>
                </div>
            )
        }
    }
    
    class App extends PureComponent {
        render() {
            return (
                <div>
                    <NavChildren>
                        <a href='./#'>logo</a>
                        <div>content</div>
                        <span>search</span>
                    </NavChildren>
                    <NavBar slotLeft={<a href='./#'>logo</a>}
                            slotCenter={<div>content</div>}
                            slotRight={<span>search</span>} />
                </div>
            )
        }
    }
    
    ReactDOM.render(< App />, document.getElementById('root'))
    
    // styled.css
    body {padding: 0;margin: 0;}
    .nav-bar {display: flex;}
    .nav-item {height: 44px;line-height: 44px;text-align: center;}
    .nav-left, .nav-right { 15%;background-color: lightpink;}
    .nav-center {flex: 1;background-color: lightblue;}

      五、通信案例

    // demo.js
    import React, { PureComponent } from 'react';
    import ReactDOM from 'react-dom';
    import propTypes from 'prop-types'
    import './style.css'
    class HeaderComponent extends PureComponent {
        constructor(props) {
            super(props)
            // 记录哪个被选中
            this.state = { currentIndex: 0 }
        }
        static propTypes = {
            header: propTypes.array.isRequired
        }
        static defaultProps = { header: [] }
        render() {
            //解构父组件传递过来的header
            const { header } = this.props;
            const { currentIndex } = this.state;
            return (
                <div className='total_header' >
                    {header.map((item, index) => {
                        return <div
                            key={item}
                            // 动态添加class
                            className={'item_header ' + (index === currentIndex ? "active" : "")}
                            onClick={this.currentClick.bind(this, index)}>
                            <span>{item}</span></div>
                    })}
                </div>
            )
        }
        currentClick(index) {
            this.setState({ currentIndex: index })
            const { currentItemClick } = this.props;
            //把index传给currentItemClick
            currentItemClick(index);
        }
    }
    class App extends PureComponent {
        constructor(props) {
            super(props);
            this.state = {
                header: ['商品', '评价', '详情','推荐'],
                currentHeader: '商品'
            }
        }
        render() {
            const { header, currentHeader } = this.state;
            return (
                <div>
                    {/* 父传子:用到props */}
                    <HeaderComponent header={header} currentItemClick={index => this.currentItemClick(index)} />
                    <h3>{currentHeader}</h3>
                </div>
            )
        }
        currentItemClick(index) {//要注意这里的index
            this.setState({ currentHeader: this.state.header[index] });
        }
    }
    ReactDOM.render(< App />, document.getElementById('root'))
    
    // styled.css
    .total_header{display: flex;}
    .item_header{flex:1;text-align: center;margin-top: 10px;}
    .item_header.active{color:red;}
    .item_header.active span{border-bottom: 2px solid red;padding:5px;}

     

  • 相关阅读:
    比率(ratio)|帕雷托图|雷达图|轮廓图|条形图|茎叶图|直方图|线图|折线图|间隔数据|比例数据|标准分数|标准差系数|离散系数|平均差|异众比率|四分位差|切比雪夫|右偏分布|
    质量控制|样本和总体|有限总体和无限总体|样本空间与变量空间|总体变异性|
    基因共线性
    q检验|新复极差法|LSD|二因素方差分析
    Tript协议|伯尔尼公约|著作权|立法宗旨|自动保护|著作权集体管理|
    两块式开头样板
    三块式开头样板
    listening-conversation|信息简写|Generally|回答|矛盾
    Listening-lecture|主旨题|术语解释|举例原则|Crash course 哔哩哔哩
    Do jobs|permanent|secure job|Move|Look after|provide sb with sth|Move|Enjoy a good time|Learn about|Be fond of|Have a clearer idea|String quarter|Be subject to|A has little with B|Pigment
  • 原文地址:https://www.cnblogs.com/davina123/p/13634350.html
Copyright © 2011-2022 走看看