react 组件

1 篇文章 0 订阅
1 篇文章 0 订阅

组件间的传值

父传子:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Father extends Component {
	constructor(props) {
		super(props);
		this.state = {
			name: 'Tom'
		};
	}

	render() {
		return (
			<div>
				<h1>这是父组件</h1>
				{/* 父传子:父组件通过子组件的props属性传值给子组件 */}
				<Son name={this.state.name} />
			</div>
		);
	}
}

class Son extends Component {
	render() {
		return (
			<div>
				<h3>这是子组件</h3>
				<p>显示父组件传递过来的值:{this.props.name}</p>
			</div>
		);
	}
}

ReactDOM.render(<Father />, document.getElementById('root'));

子传父:

  1. 在父组件中定义方法,抛出形参;
  2. 给子组件绑定方法;
  3. 在子组件中用 this.props.方法名 调用 方法同时传入实参。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Father extends Component {
	constructor(props) {
		super(props);
		this.state = {
			name: 'Tom',
			iNum: 0,
		};
	}

	{/* 1.父组件定义一个传参的方法: */}
	fnGetData = iNum => {
		this.setState({
			iNum
		});
	};

	render() {
		return (
			<div>
				<h1>这是父组件</h1>
				<p>显示子组件传递过来的值:{this.state.iNum}</p>
				{/* 2.将方法绑定在子组件身上: */}
				<Son fnGetData={this.fnGetData} />
			</div>
		);
	}
}

class Son extends Component {
	render() {
		return (
			<div>
				<h3>这是子组件</h3>
				{/* 3.在子组件中调用这个方法,同时传一个值进去: */}
				<input type="button" onClick={() => this.props.fnGetData(10)} />
			</div>
		);
	}
}

ReactDOM.render(<Father />, document.getElementById('root'));

小结:父子组件传值皆是用this.props接收。

同辈组件间的传值:

方法一 (较常用):利用父组件做中转,传值过程为:子组件1 → 父组件 → 子组件2

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Father extends Component {
	constructor(props) {
		super(props);
		this.state = {
			name: 'Tom',
			iNum: 0,
		};
	}

	fnGetData = iNum => {
		this.setState({
			iNum
		});
	};

	render() {
		return (
			<div>
				<h1>这是父组件</h1>
				{/* 2.将方法绑定在子组件身上: */}
				<Son fnGetData={this.fnGetData} />
				<Son2 iNum={this.state.iNum} />
			</div>
		);
	}
}

class Son extends Component {
	render() {
		return (
			<div>
				<h3>这是子组件一</h3>
				<input type="button" value="传值给父组件" onClick={() => this.props.fnGetData(10)} />
			</div>
		);
	}
}

class Son2 extends Component {
	render() {
		return (
			<div>
				<h3>这是子组件二</h3>
				<p>显示子组件一传递过来的值:{this.props.iNum}</p>
			</div>
		);
	}
}

ReactDOM.render(<Father />, document.getElementById('root'));

方法二 (不常用):实例化系统模块events中的EventEmitter类,通过emit发送数据,通过on接收数据
例子:子组件二 传值给 子组件一

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

// 1.导入events模块中的EventEmitter类:
import { EventEmitter } from 'events';

// 2.实例化一个EventEmitter对象:
let bus = new EventEmitter();

class Father extends Component {    
    render() {
        return (
            <div>
                <h1>这是父组件</h1>
                <Son01 />
                <Son02 />
            </div>
        );
    }
}

class Son01 extends Component {
    constructor(props){
        super(props);
        this.state = {
            msg:''
        }
    }

	// 4.生命周期中监听,接收数据:
    componentDidMount(){
        bus.on('send',dat=>{
            this.setState({
                msg:dat.msg
            })
        })
    }

    render() {
        return (
            <div>
                <h3>这是子组件一</h3>
                <p>显示子组件二传递过来的值:{ this.state.msg }</p>
            </div> 
        );
    }
}

class Son02 extends Component {
    // 3.通过实例化的bus对象的emit方法来发送数据,'send' 相当于是一个自定义事件名称
    fnSend=()=>{
        bus.emit('send',{
            msg:'数据内容为: 我来自子组件二'
        })
    }

    render() {
        return (
            <div>
                <h3>这是子组件二</h3>
                <input type="button" value="传值给子组件一" onClick={ this.fnSend } />
            </div> 
        );
    }
}

ReactDOM.render(<Father />, document.getElementById('root'));

redux

Demo 链接

类似于 Vue 中的 VueX。redux 使用时:

  • 每个 redux 组件都需要导入 store。
  • 想跟踪 store 的变化,需要订阅 store,且在组件销毁时要撤销订阅。

原理:
原理图

语法:

  • 下载:
yarn add redux
  • 新建src/store/index.js文件:(写法固定)
// 导入创建 store 的方法:
import { createStore } from 'redux';
// 导入仓库数据:
import reducer from './reducer';
// 创建 store:
let store = createStore(reducer);
// 导出 store:
export default store;
  • 新建src/store/reducer.js文件:
// 数据中心的初始数据:
const oDefaultData= {
	"aList":[],
	"sTodo":""
};

// state 参数是仓库的数据;action 参数是由 store 传递进来的数据变更
let reducer = (state = oDefaultData, action)=>{
	
	// 处理工单:
	if (action.type === "add_goods") {
        let aNewState = JSON.parse(JSON.stringify(state)); // 深拷贝
        aNewState.push(action.value);
        return aNewState;
    }

	return state;
}
export default reducer;
  • 修改 store 中的数据:
// store.dispatch() 将数据变更传到数据中心
fnAdd=(e)=>{
	store.dispatch({
		type:'add_goods',
		value:target.value
	});
}

<Button onClick={() => this.fnAdd(item)}>加入购物车</Button>
  • 在组件中使用:
// 导入store/index.js 文件:
import store from './store';

class TodoList extends Component{
	constructor(props){
		super(props);
		
		// store.getState() 获取数据中心的数据:
		this.state = store.getState();
	}
}

例🌰子:
图示:
样式图

  • src/store/index.js文件(写法固定):
// 导入创建store的方法
import { createStore } from 'redux';
// 导入数据仓库
import reducer from './reducer';
// 创建store
let store = createStore( reducer );
// 导出store
export default store;
  • 新建public/data.json文件(模拟 axios 请求数据,实际项目中不用新建):
{
    "aList":["学习html","学习css","学习javascript","学习jquery","学习vue"],
    "sTodo":""
}
  • 新建src/store/actiontype.js文件(能更好地纠错):
const CHANGE_VAL = 'change_val';
const ADD_LIST = 'add_list';
const DEL_LIST = 'del_list';
const INIT_DATA = 'init_data';

export { CHANGE_VAL, ADD_LIST, DEL_LIST, INIT_DATA }
  • src/store/reducer.js文件:
import { CHANGE_VAL, ADD_LIST, DEL_LIST, INIT_DATA } from './actiontype';

let oDefaultData = {
    aList:[],
    sTodo:''
}

/**
* @description:数据仓库的函数
* @param {type} state 仓库的数据
* @param {type} action 由 store 传递进来的数据变更
* @return: 数据中心的最新值
*/
let reducer = (state = oDefaultData, action) => {

    // 1、接收一个修改数据的工单
    if(action.type === CHANGE_VAL){
       // 将state深拷贝一份
       let oNewState =  JSON.parse( JSON.stringify( state ) );
       oNewState.sTodo = action.value;
       return oNewState;
    }

    // 2、接收一个增加数据的工单
    if(action.type === ADD_LIST){
        let oNewState =  JSON.parse( JSON.stringify( state ) );
        // 判断输入框的值是否为空或者纯空格
        if(oNewState.sTodo.trim() === ''){
            alert('请输入计划内容!');
            // sTodo可能是纯空格,将它清空一下
            oNewState.sTodo = '';            
            return oNewState;
        }else{
            // 将sTodo的值通过push方法放到数组中
            oNewState.aList.push( oNewState.sTodo.trim() );
            // 清空输入框的值
            oNewState.sTodo = '';
            return oNewState;
        }  
     }
     
     // 3、接收一个删除数据的工单
      if (action.type === DEL_LIST) {
        let oNewState = JSON.parse(JSON.stringify(state));
        // 删除oNewState里面的数组的某个成员:
        oNewState.aList.splice(action.value, 1);
        return oNewState;
    }
    
    // 4、接收一个初始化数据的工单
    if (action.type === INIT_DATA) {
        // 直接返回传递过来的新对象,这个对象会替换数据中心的对象,从而作为数据中心最新的值
        return action.value;
    }
    
    return state;
}

export default reducer
  • src/index.js文件:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import './main.css';
import axios from 'axios';
import store from './store'
import { CHANGE_VAL, ADD_LIST, DEL_LIST, INIT_DATA } from './store/actiontype';

class Todolist extends Component{
   
    constructor(props){
        super(props);
        // 1.1、获取数据中心的数据:
        this.state = store.getState();
        // 1.2、订阅数据中心的修改
        // subscribe 执行后有一个返回值(返回值是一个取消订阅的方法)
        this.unsubscribe = store.subscribe(this.fnStoreChange)
    }
    
    // 1.3、每次数据修改时,重新去数据中心拿一次数据。然后设置到state中
    fnStoreChange=()=>{
        this.setState( store.getState() )
    }
    
	// 1.4、在组件销毁之前取消数据中心的定义,从而优化组件
    componentWillUnmount() {
        this.unsubscribe();
    }
    
	// 组件挂载到页面 → 请求数据→ 数据设置到数据中心
    componentDidMount() {
    	// 模拟 axios 请求:
        axios.get('/data.json').then(dat => {
            store.dispatch({
                type: INIT_DATA,
                value: dat.data
            })
        })
    }
  
  	// 2.1、创建工单(修改数据中心的数据):
    fnChange=(e)=>{
        let action = {
            type: CHANGE_VAL,
            value: e.target.value
        }
        // 2.2提交工单:
        store.dispatch(action);
    }

	// 创建并提交工单(增加数据):
    fnAdd=()=>{
        store.dispatch({
            type: ADD_LIST
        });
    }

	// 创建并提交工单(删除数据):
    fnDel = (i) => {
        store.dispatch({
            type: DEL_LIST,
            value: i
        });

    }
    
    render() {
        let { aList, sTodo } = this.state;
        return (
            <div className="list_con">
                <h2>To do list</h2>
                <input type="text" value={sTodo} onChange={this.fnChange} className="inputtxt" />
                <input type="button" name="" value="增加" id="btn1" className="inputbtn" onClick={this.fnAdd} />

                <ul id="list" className="list">
                    {
                        aList.map((item, i) => (
                            <li key={i}><span>{item}</span><b className="del" onClick={() => this.fnDel(i)}>删除</b></li>
                        ))
                    }
                </ul>

            </div>
        )
    }
}

ReactDOM.render(<Todolist />, document.getElementById('root'));

react-redux

Demo 链接

react-redux 是对 redux 的优化。

语法:

  • 下载:
yarn add react-redux
  • src/index.js文件:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
import { Provider } from 'react-redux';
import store from './store';

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('root'));
  • src/app.js文件:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
import { Provider } from 'react-redux';
import store from './store';

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('root'));
  • 使用时:
import React from 'react';
// 导入 connect 高阶组件来连接数据中心:
import { connect } from 'react-redux';

const List = props => (
    <div>
       {
			props.aList.map...
		}
		<button onClick={ () => { props.fnAddCart(item) }}>按钮</button>
    </div>
);

// 映射一般的属性和方法到 props 属性上:
const mapStateToProps = (state) => { aList: state.aList }
// 映射操作 redux 的方法到props 属性上:
const mapDispathtchToProps = dispatch => { fnAddCart(goods){ dispatch({
	type:'ADD_GOODS',
	goods:goods
}) } }

// 导出 connet 方法的返回值,方法将组件名作为参数传入( mapDispatchToProps 没有定义时可以用 null 代替):
export default connect(mapStateToProps,mapDispatchToProps)(List);

跳舞

组件的 children 属性

react 中组件的 children 属性类似于 vue 中的插槽。

  • 组件以标签使用时,可以用单标签的方式,也可以用双标签的方式。
  • 如果以双标签的方式使用:在双标签中插入内容,那么组件的 props 上就有了 children 属性,children 属性值就是组件标签中间的内容。
  • children 属性和 props 其它属性一样,可以是文本、jsx 对象、函数或组件。
  • 属性是一般的本文:
// 定义组件
class Child extends Component {
	render(){
		return (
			<div>组件的 children 属性:{ this.props.children }</div>
		);
	}
}

// 使用组件
<Child>123</Child>
  • 属性是函数:
// 定义组件
class Child extends Component {
	render(){
		return (
			<p>组件的 children 属性</p>
			<input type="button" onClick={ this.props.children } value="按钮" />
		);
	}
}

// 使用组件
<Child>{ ()=>{ alert('Hello World!') } }</Child>

跳舞

组件功能的复用

如果多个组件的部分功能相同,我们可以将这些部分功能封装成一个组件来达到组件功能的复用。

方法一:render-props模式

import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import cat from './cat.png'; 

class Mouse extends Component{
	// 1.定义鼠标的位置:
    constructor(props){
        super(props);
        this.state = {
            x:0,
            y:0
        }
    }
	
	// 2.定义mousemove事件触发的fnGetMouse方法,将鼠标的坐标值设置到state中:
    fnGetMouse=(e)=>{
        this.setState({
            x:e.clientX,
            y:e.clientY
        })
    }

    // 3.在window对象上绑定mousemove事件,发生事件时触发fnGetMouse方法:
    componentDidMount(){
        window.addEventListener('mousemove',this.fnGetMouse)
    }

    // 4.在组件销毁之前,解除组件内部的事件绑定来优化组件
    componentWillUnmount(){
        window.removeEventListener('mousemove',this.fnGetMouse)
    }

    render(){
        // let {x,y} = this.state;
        // return <p>鼠标的位置是:x:{x}---y:{y}</p>
        // 返回一个组件props上面的方法的调用:
        // return this.props.fnShow(this.state);
        // 优化:
        return this.props.children(this.state);
    }
}


// 定义一个图片组件,调用Mouse组件实现图片跟随鼠标移动:
function Cat(props){
    return <img src={ cat } alt="猫" style={{'position':'fixed','left':props.x,'top':props.y}} />
}

// 渲染的是Mouse组件,Mouse组件里面返回一个Cat组件,同时将Mouse组件的state传给Cat组件,Cat组件就获取了Mouse组件的鼠标的功能。这种用法叫做 render-props模式:
// ReactDOM.render(<Mouse fnShow={ oMouse=><Cat x={ oMouse.x } y={oMouse.y} /> } />, document.getElementById('root'));

// 优化:用children属性来实现render-props模式
ReactDOM.render(<Mouse>{ oMouse=><Cat { ...oMouse } /> }</Mouse>, document.getElementById('root'));

方法二:高阶组件(HOC)

import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import cat from './cat.png';

// 定义一个高级组件(高级组件的名称习惯在前面加一个with)
function withMouse(Comp){
    class Mouse extends Component{
        constructor(props){
            super(props);
            this.state = {
                x:0,
                y:0
            }
        }
    
        fnGetMouse=(e)=>{
            this.setState({
                x:e.clientX,
                y:e.clientY
            })
        }
    
        // 在组件挂载到页面之后,在window对象上绑定mousemove事件
        // 关联一个方法,这个方法通过事件对象拿到鼠标的位置,同时设置到state中
        componentDidMount(){
            window.addEventListener('mousemove',this.fnGetMouse)
        }
    
        // 在组件销毁之前,解除组件内部的事件绑定来优化组件
        componentWillUnmount(){
            window.removeEventListener('mousemove',this.fnGetMouse)
        }
    
        render(){
            return <Comp {...this.state} />
        }
    }
    return Mouse;   
}

// 定义一个显示一张图片的组件
function Cat(props){
    return <img src={ cat } alt="猫" style={{'position':'fixed','left':props.x,'top':props.y}} />
}

// 使用高阶组件
let WithMouseCat = withMouse( Cat );

ReactDOM.render(<WithMouseCat />, document.getElementById('root'));

跳舞

组件的更新机制

父组件向子组件传值,若传递的是相同的值子组件就没必要更新了。
我们可以想办法使父组件的执行不触发子组件的执行,从而阻止一些无意义的更新。

方法一、纯组件 PureComponent

使用:

  • 导入PureComponent
  • 创建子组件时用PureComponent代替Component
import React,{ Component,PureComponent } from 'react';
import ReactDOM from 'react-dom';

class Father extends Component{
    constructor(props){
        super(props);
        this.state = {
            iNum:10
        }
    }

    fnAdd=()=>{
        this.setState(state=>({iNum:state.iNum+1}))
    }

    render(){
        console.log('--父组件更新了--');
        return (
            <div>
                <p>{ this.state.iNum }</p>
                <input type="button" value="递增" onClick={ this.fnAdd } />
                <Son />
            </div>
        )
    }
}

// 使用纯组件来创建子组件,优化它的更新机制
class Son extends PureComponent{
    render(){
        console.log('--子组件更新了--');
        return (
            <div>
                <p>这是子组件</p>
            </div>
        )
    }
} 

ReactDOM.render(<Father />, document.getElementById('root'));

方法二、生命周期方法 shouldComponentUpdate

使用生命周期函数shouldComponentUpdate,在生命周期函数中判断两个值是否相同。相同,则返回true,组件更新;不同,则返回false,组件不更新。

import React,{ Component } from 'react';
import ReactDOM from 'react-dom';

//let INUM03 = 10;

class Father extends Component{
    constructor(props){
        super(props);
        this.state = {
            iNum:0,
            //aList:[{id:2015,value:1},{id:2016,value:2},{id:2017,value:3}]
        }

        this.iNum02 = 20;
    }

    fnRandom=()=>{
       this.setState({
           iNum:Math.floor(Math.random()*2)
       })
    }

    render(){
        console.log('--父组件更新了--');
        return (
            <div>
                {/* <ul>{this.state.aList.map((item,i)=><li key={ item.id }>{ item.value }</li>)}</ul> */}
                <p>{ this.state.iNum }</p>
                <input type="button" value="生成0或者1" onClick={ this.fnRandom } />
                <Son iNum={ this.state.iNum }  />
            </div>
        )
    }
}

class Son extends Component{
    // shouldComponentUpdate方法中通过nextProps参数拿到props最新的值
    // 通过this.props拿到props之前的值
    shouldComponentUpdate(nextProps){
        return this.props.iNum !== nextProps.iNum;
    }

    render(){
        console.log('--子组件更新了--');
        return (
            <div>
                <p>这是子组件</p>
                <p>显示父组件传递的值:{ this.props.iNum }</p>
            </div>
        )
    }
} 

ReactDOM.render(<Father />, document.getElementById('root'));

跳舞

组件性能的优化

1、尽量减少state里面的变量,有些变量可以存储在组件属性this上,或者存储在组件外面。
例如:

let INUM = 10// 存储在组件的外面

class Father extends Component {
	construtor(props){
		super(props);
		this.state = {
			iNum: 0;
			aList: [{id:1,value:1},{id:2,value:2}]
		}
		this.iNum2 = 20; // 存储在组件的属性上
	}
}

2、可以使用纯组件PureComponent或者shouldComponentUpdate阻止不必要的更新。
3、列表渲染中,尽量使用不重复且不变的key值来优化组件的diff算法。
4、。。。。。。
开心
你可能需要:vue 父子组件传值vue路由传参(编程式导航)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值