滴水能把石穿透,萬事功到天然成——zZ先森react
話很少說,直接上乾貨!bash
父組件調用子組件,經過屬性傳遞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
React.createContext()
;// 建立一個上下文對象(REACT中的 CONTEXT API【最新】)
import React from 'react';
const ThemeContext = React.createContext();
export default ThemeContext;
複製代碼
ThemeContext.Provider value={...}
return{
<ThemeContext.Provider
value=({
//須要提供的上下文信息
...state
})>
</ThemeContext.Provider>
}
複製代碼
技巧: 須要用到的上下文,咱們通常設置爲祖先元素的狀態,這樣後期只要修改狀態,觸發getInitialState 鉤子函數執行並從新渲染, 這樣就會從新設置上下文中的信息,後代組件也從新渲染,並拿到最新的信息。3d
context=React.useContext(ThemeContext)
//經過HOOKS函數 解構出上下文中所需的方法或屬性
const { handle } = useContext(ThemeContext);
複製代碼
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>;
}
}
複製代碼
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 });
}
};
複製代碼
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