美文网首页
React 学习笔记 03 事件 + 组件

React 学习笔记 03 事件 + 组件

作者: Upcccz | 来源:发表于2019-04-13 01:44 被阅读0次

事件

绑定事件
import React,{Component} from 'react'
import ReactDOM from 'react-dom'

class Hello extends Component {
    constructor(){
        super()
        this.state = {}
    }
    // 绑定事件需要使用驼峰的方式
    render(){ 
        return (
            <div onClick={ function(){console.log('ok')}}>
                Test
                <button onClick={this.myClick}>按钮1</button>
                <button onClick={()=> this.show('传参')}>按钮2</button>
            </div>
        )
    }

    // <button onClick={this.myClick()}></button> 
    // 如果加小括号 等于直接调用 不需要点击的
    // 因为render也在生命周期中 使用这个组件的时候 就相当于创建了一个实例 
    // 这个组件实例必然就会调用render函数 必然就会解析return 的虚拟dom
    // 在解析这个应该存放变量的地方的时候 这个函数没有return值 但是这行必然会随着解析而执行
    // 所以这么写 不点击也会触发 
    myClick(){
        console.log('ok2')
    }

    show = (arg1)=>{
        console.log(arg1) // '传参'
    }
    // show这种绑定事件的方法最常用 在jsx必须使用箭头函数
}
事件绑定2

也可以使用bind返回一个函数,用变量接受,在虚拟dom中直接this.xxx

class Parent extends React.Component {
    constructor(props) {
        super(props);
        // handleClick 当做函数指针
        this.handleClick = this.handleClick.bind(this);
    }
    render(){
        return <div onClick={this.handleClick}></div>
    }

    handleClick(){
        console.log('111')
    }
}
使用this.setState修改state上的值
import React,{Component} from 'react'
import ReactDOM from 'react-dom'

class Hello extends Component {
    constructor(){
        super()
        this.state = {
            msg:"哈哈哈",
            num: 1
        }
    }
    render(){ 
        return (
            <div>
                <button onClick={()=> this.show()}>按钮2</button>
                <p> { this.state.msg }</p>
            </div>
        )
    }

    show = () => {
        // 不能直接赋值来修改(不会立即渲染视图) 应该使用setState方法
        // this.state.msg = '你好'

        this.setState({msg:"你好"}) 
        // 只会修改对应的状态msg  并不会影响到state中的num属性 

        console.log(this.state.msg) // '哈哈哈'
        // 因为 this.setState() 是异步更新DOM的 console语句会率先执行完毕

        // 如果想立即使用修改后的值 应该使用setState的第二个参数:回调函数
        // 这个回调函数修改完成后 被调用 == vue.$nextTick()
        this.setState({msg:"show"},function(){
            console.log(this.state.msg) // show
        }) 
    }
}
React的数据绑定
// React 默认只有单向数据流 没有像v-model那样的双向数据绑定
// React中 如果将表单元素中的value与state中的数据绑定
// 当状态(state中的数据)变化的时候 --> 会触发页面的更新
// 但是 在页面触发改变的时候(文本框输入的时候) 不会同步state中的数据更新
// 需要程序员绑定onChange事件拿到最新的文本框中的值 
// 手动调用this.setState()去更新state中的数据

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

class Hello extends Component {
    constructor(){
        super()
        this.state = {
            msg:"哈哈哈",
        }
    }
    render(){ 
        return (
            <div>
                <input type="text" value={this.state.msg} onChange={(e)=>this.changeValue(e)} ref="txt"/>
                <input type="text" ref={el=>this.myInput = el}/>
            </div>
        )
    }
    // 设置ref为字符串是过时了的API 应该设置为一个回调函数

    changeValue = (e)=> {
        this.setState({
            msg: e.target.value 
        })
        // 或者使用ref属性暴露dom元素
        // this.setState({
        //     msg: this.refs.txt.value / this.myInput
        // })
    }
}
React组件的生命周期
// 生命周期函数
// 组件创建阶段 : 只执行一次
componentWillMount(){}
render(){}
componentDidMount(){}

// 组件运行阶段 : 根据状态的改变触发0或多次
componentWillReceiveProps(){}
shouldComponentUpdate(){}
componentWillUpdate(){}
render(){}
componentDidUpdate(){}

// 组件销毁阶段 : 只执行一次
componentWillUnmount(){}

流程
1.创建时期,第一个执行的函数是constructor(),定义state
2.组件挂载之前触发componentWillMount,此时页面还未渲染。
3.执行render函数,创建虚拟dom,将虚拟dom挂载到真实dom上去
4.完成挂载时,触发componentDidMount函数,页面已经被渲染。

5.1 如果属性(props)改变,接受的传参改变了,触发componentWillReceiveProps函数,然后触发shouldComponentUpdate函数
5.2 如果状态(state)改变,直接触发shouldComponentUpdate函数

6.1shouldComponentUpdate函数如果return false 重新回到运行中的状态,页面没有任何更新
6.2shouldComponentUpdate函数如果return true 即代表页面即将发生更新,继续走下面的流程,不定义默认返回true

7.组件更新前,触发componentWillUpdate函数。
8.执行render函数,比较更新虚拟dom,重新渲染页面
9.组件完成更新后,触发componentDidUpdate函数

10.组件被销毁之前触发componentWillUnmount函数,调用ReactDOM.unmountComponentAtNode(document.getElementById('app'))可以销毁一个组件。

注意
1.this.forceUpdate()强制刷新会跳过shouldComponentUpdate钩子函数
2.父组件只要调用setState函数 子组件也会更新 不管子组件有没有使用到父组件中的数据 但是可以在子组件中的shouldComponentUpdate进行拦截
3.因为父组件状态改变了 如果没有在shouldComponentUpdate中比较拦截 就会走下面的流程 必然会再次触发render函数
4.触发了render函数 子组件就会触发更新 componentWillReceiveProps钩子就会触发
5.PureComponent可以避免这一点,如果子组件都没有用到父组件中的数据 那么子组件比对state必然没有改变
6.不要在shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate函数中调用setState就会造成死循环

PureComponent

根组件在setState的时候会触发render函数,也会同时触发所有组件的更新,这是不愿意看到的,也会影响性能。

父组件有时候的更新状态是希望子组件更新的,有时候是不希望的(没有修改影响子组件的状态的时候),所以需要在shouldComponentUpdate中具体分析

class myCom extends React.PureComponent {

}

// PureComponent 与 Component 的差异 体现在shouldComponentUpdate上

// PureComponent 会将nextState 和 nextProps (shouldComponentUpdate的参数)
// 与当前的 state 和 props 去比较 如果不同 则返回true 如果相同就返回false 
// 而Component的 shouldComponentUpdate 默认返回true

// 但是  PureComponent 也只比较 state 和 props 的第一层直系属性

// 因为 如果程序员 使用state定义了一个嵌套很深的json  此时一层层去比较就会太浪费性能
父子组件通过props传值元素(使用函数)
class ChildInput extends Component {
    render(){
        let pInput = this.props.parentIpt //2.2子组件接受父组件暴露的元素
        return (
            <input type="text" ref={el=>this.props.getChildInput(el)} />
            // 1.2子组件通过el传值给props接受到的函数 将input暴露给父组件 ref中函数的形参就是本身元素的指针el
        )
    }
}

class ParentInput extends Component {
    render(){
        // 1.1通过传递一个赋值接受ref的函数给子组件
        return (
            <div>
                <input type="text" value="2.1 父组件把自己的dom元素暴露给子组件" ref={el=>this.myInput =el}/>
                <ChildInput getChildInput={this.getChildInput.bind(this)} 
                    parentIpt = {this.myInput} ref={el=>this.chilidComp=el}>
                </ChildInput>
                <button onClick={this.setFocus.bind(this)}>点击让子组件的input聚焦</button>
            </div>
        )
        // 3.3 可以暴露这个组件给父组件 像上面的ref={el=>this.chilidComp=el}一样 但是不介意使用
        // 因为这样 父组件中的chilidComp这个变量代表了整个子组件 会占用很大的空间 性能不好
    }

    getChildInput(el){
        // 1.3子组件执行render的时候 就会触发传过去的getChildInput函数 把el作为参数传到父组件
        // 这样父组件就取到了子组件暴露的dom元素 然后赋值给自己的变量
        this.cInput = el;
    }

    setFocus(){
        this.cInput.focus()
    }
}
父子组件内容分发

class Child extends Component {
    render(){
        return (
            
            <div>子组件的div
                {this.props.children[1]} --- 这是span元素
                {this.props.children[0]} --- 这是p元素
                <hr/>
                {this.props.children} ---将整个分发的内容放在这里
            </div>
        )
    }
}

class Parent extends Component {
    render(){
        return (
            <div>
                <Child>
                    <p>父组件中的p元素</p>
                    <span>父组件中的span元素</span>
                </Child>
            </div>
        )
    }

    // 上面这种写法 p/span元素是不会分发到子组件中区的 
    // 因为 这个子组件会整个代替渲染父组件中的Child组件的位置 
    // 子组件没有p/span元素 所以要在子组件中接受父组件的分发
}

// 或者传一个虚拟dom对象给子组件 做分发
class Child extends Component {
    render(){
        return (
            
            <div>子组件的div
                {this.props.headDom} --- 展示父组件给的h1
            </div>
        )
    }
}

class Parent extends Component {
    render(){
        return (
            <div>
                <Child headDom={<h1> 给你一个h1去展示吧 </h1>}>
                </Child>
            </div>
        )
    }
}
Context -- 用于嵌套过深的组件传值 避免一层层的传递
// 有三个组件 A 包含 B ,B包含 C 
// 如果A组件想传值给C组件 要使用props传给B 再传给C

// 这种传值 可以使用 Context 来简化

const ValContext = React.createContext('默认值')
// 或者

const {Provider,Consumer} = React.createContext('默认值')
// 这样下面 就可以使用Provider Consumer 来充当根元素 而不是 ValContext.Provider

// 创建Context组件 充当第一层级元素的根元素 并使用ValContext.Provider 供应者
// 并充当需要接受数据的组件的根元素  并使用ValContext.Consumer 消费者

class Ccom extends Component {
    render(){
        return (
            <ValContext.Consumer>
                展示从A传下来的值 函数中的参数value就是传来的值 
                函数返回一个虚拟dom元素 就可以展示传来的值在该组件上了
                {value=> <p>我接受到A传来的值 : {value}</p>}
            </ValContext.Consumer>
        )
    }
}

class Bcom extends Component {
    render(){
        return (
            <Ccom></Ccom>
        )
    }
}

class Acom extends Component {
    render(){
        return (
            <ValContext.Provider value="我要传给c的值">
                <Bcom></Bcom>
            </ValContext.Provider>
        )
    }
}
Fragments 可以聚合一个子元素列表 并不添加额外节点

看起来就是一个空标签,类似vue中的template充当根标签 但是不增加额外节点

render(){
    return (
        <>
            <ChildA/>
            <ChildB/>
        </>
    )
}

// 或者 因为空标签语法不接受 key 如果你要遍历的话 就会报警告 
// React.Fragment 能够接收key 也是唯一只能接受key

render(){
    return (
        {
            this.list.map(item=>(
                <React.Fragment key={item.id}>
                    <ChildA/>
                    <ChildB/>
                </React.Fragment>
            ))
        }
    )
}
Portals 将子节点渲染到父组件之外的节点上

比如一个有遮罩的模态框,必然需要添加在body上。

const bodyEl = document.getElementsByTags('body')[0]

class Modal extends React.Component {
    constructor(props) {
        super(props);
        this.el = document.createElement('div');
    }

    componentDidMount() {
        bodyEl.appendChild(this.el);
    }

    componentWillUnmount() {
        bodyEl.removeChild(this.el);
    }

    render() {
        return ReactDOM.createPortal(
            this.props.children, // 下面的Modal中的Child
            this.el,
        );
    }
}
// 将模态框使用在父组件中  此时的Modal组件相当于一个中转 使用Portal则挂载在父组件上但可以不受父组件根元素div的限制
// 然后再modal中将child(即模态框的实际内容)添加在创建的div上 再将div(模态框)添加到body上

class Parent extends React.Component {
  render() {
    return (
      <div>
        <Modal>
          <Child />
        </Modal>
      </div>
    );
  }
}

function Child() { // 无状态组件 
  return (
    <div className="modal">
      <button>Click</button>
    </div>
  );
}
高阶组件

高阶组件就是一个函数,且该函数接受一个组件作为参数,然后返回一个新的组件。相当于是一个工厂用来增加组件的,a,b组件都需要一把锤子,就将其传值给锤子工厂(高阶组件)去给a,b组件装一把锤子,这里的锤子就是公共部分,a,b都需要用到的。

高阶组件的参数:需要混入公共功能的组件
高阶组件的返回:已经增强过的组件


class Acom extends React.Component {
  render() {
    return (
      <div>
        <p>我是a,我需要一把{this.props.msg}</p>
        <button onClick={this.props.show}>展示锤子吧!!!</button>
      </div>
    );
  }
}

class Bcom extends React.Component {
  render() {
    return (
      <div>
        <p>我是b,我也需要一把{this.props.msg}</p>
        <button onClick={this.props.show}>展示锤子吧!!!</button>
      </div>
    );
  }
}

function myHighCom(Com){
    // 匿名类 相当于匿名函数
    return class extends React.Component {
        constrcutor(props){
            super(props)
            this.state = {
                wuqi:"锤子"
            }
        }

        render(){
            return <Com wuqi={this.state.wuqi} show={this.show}></Com>
        }

        show(){
            console.log('展示我的锤子')
        }

        componentDidMount(){
            console.log('我们在生命周期钩子函数里面也要做同样的事情')
        }
    }
}

const AplusCom = myHighCom(Acom)
const BplusCom = myHighCom(Bcom)

// 使用增强过的组件

class Parent extends React.Component {
  render() {
    return (
      <div>
        <AplusCom/>
        <hr/>
        <BplusCom/>
      </div>
    );
  }
}

ReactDOM.render(<Parent />,document.getElementById('app'))

相关文章

  • React 学习笔记 03 事件 + 组件

    事件 绑定事件 事件绑定2 也可以使用bind返回一个函数,用变量接受,在虚拟dom中直接this.xxx 使用t...

  • React组件学习笔记——慕课网

    React组件学习笔记——慕课网 React组件慕课网视频:传送门 第1章 初识React 1.1 React基本...

  • React 官网笔记 更新于:2020-12-15

    React 官网学习笔记 所有 React 组件都必须像纯函数一样保护它们的 props 不被更改 在 React...

  • (1)React的开发

    1、React项目架构搭建2、JSX语法3、React组件化开发4、React组件间通信5、React中的事件6、...

  • react基础

    认识React 组件 Jsx的引用 插值符号 组件的数据状态 组件的样式 组件的事件 组件的生命周期 React的...

  • React学习笔记—React组件

    1、Component React的首要思想是通过组件(Component)来开发应用。所谓组件,简单说,指的是能...

  • react学习笔记

    React笔记 创建项目 入口文件 组件构成 子组件对父组件的校验等 React的虚拟Dom实现原理 state数...

  • 学习笔记:React组件

    一、前沿 React主要的作用是提高前端开发MVC架构中的View开发效率。提高前端页面的代码复用率、简化前端数据...

  • 【Vue学习笔记】—— 组件之间传递数据 { }

    学习笔记 作者:oMing Vue 组件1.通过绑定传递数据(父组件 ——》 子组件) 2.通过事件传递数据 ...

  • React父子组件间通信的实现方式

    React学习笔记之父子组件间通信的实现:今天弄清楚了父子组件间通信是怎么实现的。父组件向子组件通信是通过向子组件...

网友评论

      本文标题:React 学习笔记 03 事件 + 组件

      本文链接:https://www.haomeiwen.com/subject/kkwfwqtx.html