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 {width: 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;}

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM