React 组件间通信

  使用props作为属性数据共享的载体,在组件之间传输;但是在react中这种传输时单向的,即父组件->子组件,想要将子组件中的数据变动让其他组件也接收到,需要做处理。

2019-7-29 更新

  1. 完善Context的嵌套属性传入解决示例;
  2. 完善Context动态更新属性解决示例;
Props

props作为react的基本概念,父组件传输到子组件的属性都保存在props中,通过this.props调取其中的属性(如果是通过函数声明,则不需要this,这个函数接受一个参数props)。

// child component
function Square(props){
    return (
	    <button
	        className="square" 
	        onClick={props.onClick}>
	        {props.name}
	    </button>
    );
}
// child component
class Board extends React.Component{
	constructor(props){
		 super(props);
		 this.state = {
		 }       
	 }
	render(){
		return (<Square name={this.props.name} onClick={()=>this.props.onClick("hello")} />);
 	}
}
// parent component
class Game extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            name:"world"
        }
    }
    handleClick(name){
		this.setState({
			name:name
		});
	}
    render(){
    	return	(<Board name={this.state.name} onClick={(name)=>this.handleClick(name)} />);
    }
}

  示例中展示父组件传递一个属性name给子组件,并传递一个回调函数onClick,用于修改name的值,点击按钮时,接收到参数"hello";在处理函数handleClick中更新父组件的属性name,从而触发视图的重新渲染,name="hello"重新传递给子组件,得到视图的更新。

在事件处理的时候,注意this对象的绑定,示例中使用了箭头函数()=>{}解决了该问题。

如果回调是这样传递的

 render(){
    return	(<Square name={this.state.name} onClick={this.handleClick} />);
 }

需要在constructor手动绑定函数的this对象

 constructor(props){
        super(props);
        this.state = {
            name:"world"
        }
        // so
        this.handleClick = this.handleClick.bind(this);
    }

  在没有关系的的组件间要共享数据,找到他们的公共的父组件处理;但是如果我们的组件嵌套很多,一级一级传递必定会造成困扰。

Context

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。 摘自React文档

  Context翻译为上下文,可以理解为给当前的组件this对象混入一个Context,类似全局对象,对象是引用类型,属性使用时因为是共享的内存地址;那么属性发生变化时,所有有用的地方属性值都已更改,只要触发重新渲染视图即可(这里已无需考虑,React已经做了这个事情)。

Context 使用的API

  • React.createContext
    创建一个Context,React渲染订阅这个Context对象的组件;在组件树中匹配离自身最近的Provider中读取当前的Context值。

  • Context.Provider
    生产者;接受一个value属性,传递给消费组件。

  • Class.contextType
    class的contextType属性挂载一个Context对象;使用this.context来使用对象上的值。

  • Context.Consumer 这里需要一个示例
    消费者;

     // child component
     function Square(){
         return (<NameContext.Consumer>
             {value=>(<button className="">
                 {value}
             </button>)}
         </NameContext.Consumer>
         );
     
     }
    

context属性传递:

	// create Context
 	const NameContext = React.createContext("name");
 	
	// child component
	class Square extends React.Component{
		 static contextType = NameContext;
	    render(){
	        return (
	            <button className="square">
	                {this.context}
	            </button>
	        );
	    }
	    
	}
	// child component
	class Board extends React.Component{
		constructor(props){
		 	super(props);
		    this.state = {
		    }       
		 }
		 render(){
		     return (<Square onClick={()=>this.props.onClick("hello")} />);
		 }
	}
	// parent component
	class Game extends React.Component {
	    constructor(props){
	        super(props);
	        this.state = {
	            name:"world"
	        }
	    }
	    handleClick(name){
			this.setState({
				name:name
			});
		}
	    render(){
	    	return	(<NameContext.Provider value="admin">
	    	    <Board onClick={(name)=>this.handleClick(name)} />
	    	</NameContext.Provider>);
	    }
	}

描述:

  1. createContext创建这个上下文对象;
  2. 在顶部组件注入作为生产者.Provider ,接受一个属性传入value给属性进行赋值。
  3. 在需要使用这些属性的组件进行混入contextType作为非函数组件的属性用于挂载Context
  4. 获取这些属性this.context进行访问。

使用组合模式/嵌套模式来解决大部分跨组件属性传递

	// child component
	function Square(props){
	        return (
	            <button className="square" onClick={props.onClick}>
	                {props.name}
	            </button>
	        );
	    
	}
	// child component
	class Board extends React.Component{
		constructor(props){
		 	super(props);
		    this.state = {
		    }       
		 }
		 render(){
		     return (<div>
		        <h3>提供测试</h3>
		        {this.props.btn}
		     </div>);
		 }
	}
	// parent component
	class Game extends React.Component {
	    constructor(props){
	        super(props);
	        this.state = {
	            name:"world"
	        }
	    }
	    handleClick(name){
			this.setState({
				name:name
			});
		}
	    render(){
	    	// 在父组件中创建JSX表达式,作为组件的属性传入,
	        let btn = (<Square name={this.state.name} onClick={()=>this.handleClick("hello")} />);
	    	return	(<Board btn={btn} />);
	    }
	}

描述:

  1. 把需要用到的组件提到属性定义位置,创建JSX表达式,作为属性传入子组件。
  2. 在需要用到的组件中作为children放到合适的位置,有点类似于slot

动态更新Context属性值

const NameContext = React.createContext({
 	    name:"admin",
 	    updateName:()=>{}
 	});
 	
// child component
 function Square(){
     return (<NameContext.Consumer>
         {({name,updateName})=>(<button className="" onClick={()=>updateName("hello")}>
             {name}
         </button>)}
     </NameContext.Consumer>
     );
 }
// child component
class Board extends React.Component{
	constructor(props){
	 	super(props);
	    this.state = {
	    }       
	    
	 }
	 render(){
	     return (<div>
	        <h3>提供测试</h3>
	        <Square />
	     </div>);
	 }
}
// parent component
class Game extends React.Component {
    constructor(props){
        super(props);
        this.updateName=name=>{
	        this.setState({
	            name:name
	        });
	    }
	    this.state = {
	        name:"admin",
	        updateName:this.updateName
	    } 
    }
    handleClick(name){
		this.setState({
			name:name
		});
	}
    render(){
    	return	(<NameContext.Provider value={this.state}>
    	    <Board />
    	</NameContext.Provider>);
    }
}

描述:

  1. 创建上下文Context属性,更新的方法(空壳方法,在顶层组件向下传入时定义逻辑);
  2. 在顶层组件Game创建系统的属性、方法定义(实现逻辑处理),value接收整个state
  3. 在需要使用的子组件中NameContext.Consumer渲染时接收传入的参数,定义、传入和接收必须保持一致。
Refs&DOM

Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建React 元素。 摘自React文档

使用场景:

  • 管理焦点,文本选择或媒体播放
  • 触发强制动画
  • 集成第三方DOM库

React.createRef()用于创建Ref,并通过ref属性附加到组件上;
this.myRef.current获得该节点的引用;

	// parent component
	class Game extends React.Component {
	    constructor(props){
	        super(props);
	        this.state = {
	            name:"world"
	        }
	        // create Ref
	        this.myRef = React.createRef();
	    }
	    handleClick(name){
			this.setState({
				name:name
			});
			console.log(this.myRef.current);
		}
	    render(){
	    	return	(<NameContext.Provider>
	    	    <Board ref={this.myRef} onClick={(name)=>this.handleClick(name)} />
	    	</NameContext.Provider>);
	    }
	}

点击按钮后打印出实例:
在这里插入图片描述

函数组件没有实例,不能使用ref属性

回调Refs

不使用createRef,传递给ref属性一个回调函数,函数的参数为组件实例或DOM元素

// parent component
class Game extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            name:"world"
        }
        // this.myRef = React.createRef();
        this.domRef = null;
        this.getDOM=dom=>{this.domRef = dom};
    }
    handleClick(name){
		this.setState({
			name:name
		});
		console.log(this.domRef);
	}
    render(){
    	return	(<NameContext.Provider>
    	    <Board ref={this.getDOM} onClick={(name)=>this.handleClick(name)} />
    	</NameContext.Provider>);
    }
}
Refs 转发

ref自动地通过组件传递到其一子组件。隔层传递;

const Board = React.forwardRef((props,ref)=>(
	<button ref={ref} onClick={()=>props.onClick("hello")}>
		{props.name}
	</button>
));

// parent component
class Game extends React.Component {
    constructor(props){
        super(props);
        this.state = {
            name:"world"
        }
        this.myRef = React.createRef();
        //this.domRef = null;
        //this.getDOM=dom=>{this.domRef = dom};
    }
    handleClick(name){
		this.setState({
			name:name
		});
		console.log(this.myRef.current);
	}
    render(){
    	return	(<NameContext.Provider>
    	    <Board ref={this.myRef} name={this.state.name} onClick={(name)=>this.handleClick(name)} />
    	</NameContext.Provider>);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

heroboyluck

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值