
滴水能把石穿透,万事功到自然成——zZ先森
话不多说,直接上干货!
基于属性传递props
父组件调用子组件,通过属性传递props,属于单向数据传递,即只能是父组件向子组件传递属性,反过来不可以。
子组件向父组件传递信息
回调函数:
父组件通过属性的方式把能操作自己状态的方法传递给子组件,然后子组件拿到方法进行操作,通过方法修改相应的信息。底层还是基于属性传递
JSONP和JSBridge的运行机制和回调函数方式类似
发布订阅: 基于$on
和$emit
实现的信息通信
手动实现一个发布订阅:
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
})
})
}
}
执行上下文进行信息传递:
把后代需要用到的属性和方法,放到祖先元素的上下文中,后代组件可以直接注册获取使用。【适用于有一个共同祖先组件】
【函数式组件 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 钩子函数执行并重新渲染, 这样就会重新设置上下文中的信息,后代组件也重新渲染,并拿到最新的信息。
- 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。后续持续总结更新!