React—複合組件信息傳遞

滴水能把石穿透,萬事功到天然成——zZ先森react

話很少說,直接上乾貨!bash

基於屬性傳遞props

父組件調用子組件,經過屬性傳遞props,屬於單向數據傳遞,即只能是父組件向子組件傳遞屬性,反過來不能夠。ide

子組件向父組件傳遞信息

回調函數:

父組件經過屬性的方式把能操做本身狀態的方法傳遞給子組件,而後子組件拿到方法進行操做,經過方法修改相應的信息。底層仍是基於屬性傳遞函數

JSONP和JSBridge的運行機制和回調函數方式相似ui

發佈訂閱: 基於$on$emit實現的信息通訊

手動實現一個發佈訂閱:this

class EventEmit{
    //事件池 {事件名:[訂閱方法...]}
    pond = {};
    $on(eventName,func){
        if(!this.pond.hasOwnProperty(eventName)){
            //每個事件名對應一系列的方法
            this.pond.eventName = [];
        }
        //訂閱方法的去重
        this.pond[eventName].some(item=>item===func) return;
        this.pond[eventName].push(func)
    }
    $emit(eventName,...rargs){
        let funArr = this.pond[eventName]||[];
        funcArr.forEach(item=>{
            item.bind(null,...args)
        })
    }
    export default new EventEmit();
}
複製代碼
import React from "react";
import EM from "./EventEmit";
class Main tetends React.component{
    state = {
        supNum:0,
        oppNum:0
    }
    handle=(type)=>{
        let {subNum,oppNum} = this.state;
        if(type==="sup"){
            this.setState({
                subNum:subNum++;
            })
        }else{
            this.setState({
                this.setState({
                    oppNum:oppNum++;
                })
            })
        }
    }
    render(){
        let {oppNum,oppNum} = this.state;
        return <main className="mainBox">
               <p>支持人數{supNum}</p>
               <p>反對人數{oppNum}</p>
        </main>
    }
    componentDidMount(){
        EM.$on("mainHandle",handle)
    }
}

class Footer extends React.component{
    render(){
        return <footer className="footerBox">
        <button onClick = {ev=>{
            EM.$emit("mainHandle","SUP");
            EM.$emit("totalHandle")
        }}>支持</button>
        <button onClick = {ev=>{
            EM.$emit("mainHandle","OPP")
            EM.$emit("totalHandle")
        }}>反對</button>
        </footer>
    }
}

export default class Vote extends React.component{
    state = {total:0}
    return {
        <div className="voteBox">
           <header className="headerBox">
               <h3>{this.props.title}</h3>
               <span>投票人數:{this.state.total}</span>
           </header>
           <Main></Main>
           <Footer></Footer>
        <div>
    }
    componentDidMount(){
        EM.$on("totalHandle",()=>{
            this.setState({
                total:this.state.tota+1
            })
        })
    }
}
複製代碼

執行上下文進行信息傳遞:

把後代須要用到的屬性和方法,放到祖先元素的上下文中,後代組件能夠直接註冊獲取使用。【適用於有一個共同祖先組件】spa

【函數式組件 HOOKS】

  • 1.建立上下文對象ThemeContext:React.createContext();
// 建立一個上下文對象(REACT中的 CONTEXT API【最新】)
import React from 'react';
const ThemeContext = React.createContext();
export default ThemeContext;
複製代碼
  • 2.在祖先上建立上下文 ThemeContext.Provider value={...}
return{
        <ThemeContext.Provider  
        value=({
            //須要提供的上下文信息
            ...state
        })>
        </ThemeContext.Provider>
}
複製代碼

技巧: 須要用到的上下文,咱們通常設置爲祖先元素的狀態,這樣後期只要修改狀態,觸發getInitialState 鉤子函數執行並從新渲染, 這樣就會從新設置上下文中的信息,後代組件也從新渲染,並拿到最新的信息。3d

  • 3.在後代組件中使用上下文信息 context=React.useContext(ThemeContext)
    //經過HOOKS函數 解構出上下文中所需的方法或屬性
    const { handle } = useContext(ThemeContext);
    複製代碼

【類組件】

  • REACT第15代版本中提供,在StrictModel模式下有警告錯誤
    • 指定後代組件能夠用到的屬性類型 static childContextTypes = {...}
    • 給後代組件提供數據接口getChildContext(){return{...}}【也屬於生命週期函數】
    • 後代註冊使用static contextTypes = {...}【聲明狀態類型必須和祖先組件提供的類型一致】
import React from 'react';
import PropTypes from 'prop-types';

class Main extends React.Component {
	// 獲取的上下文信息掛載到實例的this.context中了(獲取的上下文信息是能夠修改的,
	可是並無影響到祖先)
	static contextTypes = {
		supNum: PropTypes.number,
		oppNum: PropTypes.number
	};
	/*constructor(props, context) {
		super(props, context);
	} */
	render() {
		return <main className="mainBox">
			<p>支持人數:{this.context.supNum}</p>
			<p>反對人數:{this.context.oppNum}</p>
		</main>;
	}
}

class Footer extends React.Component {
	static contextTypes = {
		handle: PropTypes.func
	};
	render() {
		return <footer className="footerBox">
			<button onClick={ev => {
				this.context.handle('SUP');
			}}>支持</button>
			<button onClick={ev => {
				this.context.handle('OPP');
			}}>反對</button>
		</footer>;
	}
}

export default class Vote extends React.Component {
	static childContextTypes = {
		supNum: PropTypes.number,
		oppNum: PropTypes.number,
		handle: PropTypes.func
	};
	getChildContext() {
		//=>第一次在getIntialState以後執行,每當祖先組件中的狀態改變,
		從新渲染的時候,此鉤子函數也會從新被執行
		return {
			supNum: this.state.supNum,
			oppNum: this.state.oppNum,
			handle: this.handle
		}
	}
	state = {
		supNum: 0,
		oppNum: 0
	};
	handle = type => {
		let { supNum, oppNum } = this.state;
		type === 'SUP' ? this.setState({ supNum: supNum + 1 }) : this.setState({ oppNum: oppNum + 1 });
	};
	render() {
		return <div className="voteBox">
			<header className="headerBox">
				<h3>{this.props.title}</h3>
				<span>N:{this.state.supNum + this.state.oppNum}</span>
			</header>
			<Main></Main>
			<Footer></Footer>
		</div>;
	}
}
複製代碼
  • React新版本規範
    • 基於ThemeContext.Provider中的value註冊上下文信息
      import React from 'react';
      import VoteMain from './VoteMain';
      import VoteFooter from './VoteFooter';
      import ThemeContext from './ThemeContext';
      
      export default class Vote extends React.Component {
      state = {
      	supNum: 0,
      	oppNum: 0
      };
      
      render() {
      	let { supNum, oppNum } = this.state;
      
      	/* 基於ThemeContext.Provider中的value註冊上下文信息 */
      	return <ThemeContext.Provider
      		value={{
      			supNum,
      			oppNum,
      			handle: this.handle
      		}}>
      		<div className="voteBox">
      			<header className="voteHeader">
      				<h3>{this.props.title}</h3>
      				<span>【{supNum + oppNum}】</span>
      			</header>
      			<VoteMain></VoteMain>
      			<VoteFooter></VoteFooter>
      		</div>
      	</ThemeContext.Provider>;
      }
      
      handle = (lx = 0) => {
      	// 支持
      	if (lx === 0) {
      		this.setState({ supNum: this.state.supNum + 1 });
      		return;
      	}
      	// 反對
      	this.setState({ oppNum: this.state.oppNum + 1 });
         } 
      };
      
      複製代碼
    • 後代組件使用上下文信息
      • 基於Consumer組件來使用上下文信息
      import React from 'react';
      import ThemeContext from './ThemeContext';
      export default class voteMain extends React.Component {
      render() {
      	return <ThemeContext.Consumer>
      		{context => {
      			let { supNum, oppNum } = context;
      			return <main className="voteMain">
      				<p>支持數:{supNum}</p>
      				<p>反對數:{oppNum}</p>
      				<p>支持率:{this.ratio(supNum, oppNum)}</p>
      			</main>;
      		}}
      	</ThemeContext.Consumer>;
               }
          ratio = (supNum, oppNum) => {
      	let total = supNum + oppNum;
      	if (total === 0) return '--';
      	return (supNum / total * 100).toFixed(2) + '%';
          }
      }
      複製代碼
      • 經過this.context
      import React from 'react';
      import ThemeContext from './ThemeContext';
      
      export default class voteFooter extends React.Component {
      static contextType = ThemeContext;
      
      render() {
      	return <footer className="voteFooter">
      		<button onClick={_ => {
      			this.context.handle(0);
      		}}>支持</button>
      
      		<button onClick={_ => {
      			this.context.handle(1);
      		}}>反對</button>
      	</footer>;
          }
       };
      複製代碼

總結

在項目中最經常使用的是基於屬性傳遞PROPS,和執行上下文進行傳遞,因爲在祖先組件放置上下文,顯得有點臃腫,因此最最經常使用的是基於公共狀態管理Redux。後續持續總結更新!code

相關文章
相關標籤/搜索