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

相关文章
相关标签/搜索