react 事件怎么调用多个函数
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react 事件怎么调用多个函数相关的知识,希望对你有一定的参考价值。
参考技术A 触摸事件:onTouchCancel\onTouchEnd\onTouchMove\onTouchStart(只会在移动设备上接受)
键盘事件:onKeyDown\onKeyPress\onKeyUp
剪切事件:onCopy\onCut\onPaste
表单事件:onChange\onInput\onSubmit
焦点事件:onFocus\onBlur
UI元素:onScroll(移动设备是手指滚动和PC的鼠标滑动)
滚动事件:onWheel(鼠标滚轮)
鼠标类型:onClick\onContextMenu(右键)\onDoubleClick\onMouseDown\onMouseEnter\
onMouseLeave\onMouseMove\onMouseOut\onMouseOver\onMouseUp
onDrag\onDrop\onDragEnd\onDragEnter\onDragExit\onDragLeave\onDragOver\onDragStart
React 学习 ---- 生命周期函数
挂载阶段
constructor -> static getDerivedStateFromProps -> render -> componentDidMount
constructor(构造函数):初始化状态,当然它还接受一个props参数,可以使用组件传递过来的props,通常是用props初始化state。
getDerivedStateFromProps: 被static 修饰,静态方法,这也意味着,在函数里面不能使用this和实例方法。它接受两个参数 props 和state,在渲染之前,根据props计算得到state,并返回state。
render(渲染):组件中render 函数,它是至关重要的,因为组件的目的就是要渲染内容到页面上,不过它返回是一个虚拟DOM, ReactDOM 根据它构建出真实的DOM渲染到页面上。它会根据props 和state 渲染出react element, 指导react渲染出真正的dom. 但总存在特殊的情况,组件不需要渲染什么东西,如验证用户有没有登录的组件,它确实不需要返回什么,我们只是根据它进行跳转到不同的页面,这时直接在render 函数中返回null.
componentDidMount(挂载完成):当真实的DOM 渲染到页面上会触发它。 需要注意的是render 函数在调用完成后,componentDidMount 并不会立刻调用,而是在当所有组件的render函数都调用完华之后,每个组件的componentDidMount 才会调用。 写一下代码会看得比较清楚,这也是我第一次明白地认识了react的挂载生命周期。
写一个Counter 和一个CounterWrapper 组件, CounterWrapper包含Counter 。CounterWrapper 为父组件,Counter 为子组件。为了更能清楚地明白生命周期函数,CounterWrapper组件多包含几个Counter. 为了对每一个Counter 进行区分,分别给它们传一个caption 和initValue 参数。 在CounterWrapper 中 由于没有state, 我只写了render函数和ComponentDidMount 函数,而在子组件Counter中,它有state, 有完整的生命周期函数,依次写了四个阶段,constructor, static getDerivedStateFromProps, render, componentDidMount, 当然这里只是console.log一下信息,看一下执行顺序
// 子组件Counter class Counter extends React.Component { constructor(props) { super(props); console.log(`进入子组件Counter的构造函数constructor ----- ${props.caption}`); this.state = { count: props.initValue || 0 } } static getDerivedStateFromProps(props) { console.log(`进入子组件Counter的getDerivatedStateFromProps ----- ${props.caption}`); } render() { console.log(`进入子组件Counter的render ----- ${this.props.caption}`); const buttonStyle = { margin: \'10px\' }; return ( <div>
<button style={buttonStyle} >+</button> <button style={buttonStyle} value={5}>-</button> <span>{this.props.caption}count: {this.state.count}</span> </div> ); } componentDidMount() { console.log(`进入子组件Counter的componentDidMount ----- ${this.props.caption}`); } } // 父组件CounterWrapper class CounterWrapper extends React.Component { render(){ console.log(\'进入父组件CounterWrapper的render 函数\'); return (<div> <Counter caption="第一个" initValue={3}></Counter> <Counter caption="第二个" initValue={6}></Counter> <Counter caption="第三个" initValue={9}></Counter> </div>) } componentDidMount() { console.log(\'进入父组件CounterWrapper的 componentDidMount 函数\'); } }
打开控制台,重点看一下componentDidMount。先进入的是父组件CounterWrapper 的render 函数,然后再进入第一个子组件的constructor, getDerivatedStateFromProps和 render, 再进入第二个子组件的constructor, getDerivatedStateFromProps和 render函数,再进入第三个子组件的constructor, getDerivatedStateFromProps和 render函数, 最后才是每一个子组件的componentDidMount, 第二个子组件的ccomponentDidMount函数, 第三个子组件的ccomponentDidMount函数,最后的最后是父组件的CompoentDidMount.
组件的挂载生命周期并不是依次调用的,render 函数后面并不是紧紧根随着componentDidMount 函数。只有当三个组件的render函数都调用完成后,三个组件的componentDidMount 才连在一起被调用。 只有当所有的子组件的componentDidMount都调用了,父组件的componetDidMount 才会被调用。在componentDidMount 函数的调用上,我一直存在误解,以为render 函数调用后,立即执行它,并认为父组件的componentDidMount优于子组件的componentDidMount 执行,真是太想当然了。
现在再来想一下为什么会有这样的设计,当使用componentDidMount 的时候,我们听得最多的一句话可能就是,在这里,你可以操作真实的DOM了,换句话说,当 组件compoentDidMount的时候,真实的DOM 已经渲染完毕了,整个页面都已经渲染完成了。整个页面就意味着整个DOM树都已经构建完成了,注意这里是整个DOM树。当React进入到组件的render 函数时,它只知道当前组件长什么样子,而不会知道整个DOM树长什么样子,所以它只能接着找到第二个组件render, 因为后面有第三个子组件,它还是不知道整个DOM树长什么样子,所以它还要找到第三个组件render, 也就是说它会把所有的组件都循环一遍,直到能够构建整个DOM树,它才会渲染真实的DOM, 这也是最为省事的办法,如果它找到一个渲染一个,后面的组件再对前面的组件有影响,它要把前面的做法再来一次,效率低下。一次渲染就OK了。 渲染之后,肯定是最深层的组件先完成,只有小的完成了才能组成大的,所以最深层的组件的componentDidMount 先执行, 最外层的最后执行。这让我们想成了JS事件处理阶段,当构建DOM树的过程中,它是捕获阶段,从外面找到最里面,而在渲染真实的DOM的时候,componentDidMount的时候,它是冒泡阶段, 从最里面到最外面。
更新阶段
getDerivedStateFromProps: 和挂载阶段用法是一致的, 它适应用一种情况:组件的状态一直受父组件props的影响, 具体看下面的实例,css 用的是bootstrap的,npm install bootstrap 或 直接cdn link。
// 子组件DirectionDisplay export class DirectionDisplay extends React.Component { state = { direction: "up", lastValue: 0 } getClasses() { return (this.state.direction === "up" ? "bg-success" : "bg-danger") + " text-white text-center p-2 m-2"; } render() { return <h5 className={this.getClasses()}> {this.props.value} </h5> } /* 接受props和state(当前组件的), 返回state. 当前组件的state只有两个属性lastValue, direction, 所以在函数返回state的时候,最好也返回这两个属性 这里要注意一点,使用if 判断,如果props没有变化的话,直接返回state, 因为update 阶段,也会调用这个生命周期函数。 */ static getDerivedStateFromProps(props, state) { if (props.value !== state.lastValue) { return { lastValue: props.value, direction: state.lastValue > props.value ? "down" : "up" } } return state; } } // 父组件 class App extends React.Component { state = {counter: 100} changeCounter = (val) => { this.setState({ counter: this.state.counter + val }) } render() { return <div className="container text-center"> <DirectionDisplay value={this.state.counter} /> <div className="text-center"> <button className="btn btn-primary m-1" onClick={() => this.changeCounter(-1)}>Decrease</button> <button className="btn btn-primary m-1" onClick={() => this.changeCounter(1)}>Increase</button> </div> </div > } }
shouldComponentUpdate() : 组件应不应该更新,它决定了一个组件是不是需要重新渲染, 如果它返回fasle, 就表示该组件不需要更新,也就不会重新渲染,它后面的生命周期函数就不会被执行。如果返回true,表示该组件需要更新, 它后面的生命周期函数才会被执行,重新渲染。 接受两个参数nextProps和nextState, 可以判断它们是不是和当前的props 和state 一致,如果一致,就是返回true,表示更新,如果不一致,则返回false,就不更新了。 现在来写一下componentShouldUpdate
// 如果父组件传递过来的value发生变化,才会返回true, 组件才需要演染。 shouldComponentUpdate(nextProps, nextState) { return (nextProps.value!== this.props.value); }
render 就是渲染组件了,这没有什么可说的。getSnapshotBeforeUpdate,在更新之前得到快照,在使用refs的时候会用到,一般也不会用到。 componentDidUpdate, 和componentDidMount 用法一致
卸载阶段,
componentwillUnmount 函数,当React 组件要从DOM树上删掉之前,对应的componentWillUnmount函数就会被调用,它适合做一些清理性的工作,就是删除这个组件之前有没有什么要清理的。注意它没有对应的did函数,因为组件删除以后,没有什么事情可以做了。
以上是关于react 事件怎么调用多个函数的主要内容,如果未能解决你的问题,请参考以下文章
如何在“react-native-router-flux”中绑定到 onPress 事件的函数中调用 Actions.xxx