前言
eg:代表代码对照
若文章有误,欢迎读者留言反馈
知识点回顾
- 插值 {} 变量,表达式,函数调用 ☞ 落脚点是值
- 动态属性 属性={ 变量 }
- 样式写法
className={ 变量 }
className={'active btn'}
className={ arr.join(' ') }
style={{ fontSize:'12px',color:变量 }}
- 循环
1 2 3 4 5 6 7 8
| <ul> {arr.map((item,index)=>{return <li key={ item.id }>{ item }</li>}) } </ul> <ul> { Object.keys(obj).map((item,index)=>{return <li key={ item }>属性:{ item },属性值:{ ob[item] }</li> }) } </ul>
|
- 组件基本认识
- 函数组件就是函数,函数名称大写,有return值,return的是jsx,函数没有实例,没有生命周期,没有状态,没有this, 函数组件我们也可以叫纯函数,参数不变的情况下,函数组件也不会变化。
- 类组件就继承react中的父类,重写了render函数,函数中有return值,return是jsx。
- 组件中jsx必须有唯一的跟标签,组件名称遵循大驼峰
- props
- 类组件中可以this.props.属性 可以获取父组件的值。
- 函数组件通过props.属性获取父组件的值。
- props只读,单项数据流,父组件更新数据,props中的数据也随之更新。props就是父组件流转给子组件的数据。
- 事件
- 语法:on + 事件类型(首字符大写) = 执行函数
- event 事件对象是混合事件对象
- react的事件对象和原生事件对象区别?
react为了更好的兼容性和跨平台。为了事件的统一管理,所有的事件绑定在document上,避免频繁解绑,提来性能。
- this问题
=>react中执行函数中默认是没有this的,需要手动传入,推荐使用箭头函数来作为我们的执行函数。
props进阶
- props.children 类似vue中的匿名插槽,获取父组件中子组件占位符闭合标签中的内容。
props校验
- props默认值
- 语法1: 组件名.defaultProps = { num:0 } // 定义默认值
- 语法2: static defaultProps = { num:0} // 只适用于类组件
受控组件和ref使用(非受控组件)
1 2 3 4 5
| state = { inputVal:'' } <input value={ this.state.inputVal } onInput={ this.handleClick }/> handleClick = (e) => { this.setState({ inputVal:e.target.value }) }
|
ref使用 简写
1 2
| <input value={ this.state.inputVal } ref={ (inputVal)=>{ this.inputVal = inputVal } }/> this.inputVal.value
|
生命周期
- 挂载阶段:constructor getDerivedStateFromProps render componentDidMount(挂载后常用)
- 更新阶段:getDerivedStateFromProps shouldComponentUpdate render getSnapshotBeforeUpdate componentDidUpdate更新后
- 销毁阶段:componentWillUnmount销毁前
- 废弃生命周期:componentWillMount 挂载前 componentWillReceiveProps props改变就执行 componentWillUpdate更新前
- 组件通信
- 父子通信
- 父组件传递属性变量或者函数给子组件,子组件使用或者调用函数来实现父子通信。
- 跨层级通信
- eventBus通信,事件总线通信
- redux
React的受控组件
受控组件[类似vue中的双向绑定v-model],input这个元素受state中某个变量的控制【但是需要自己实现】
使用方法:
- 定义一个state中变量,元素上使用value属性,把这个值绑定上去
- 元素上添加onChange事件,当前元素进行修改的使用,state变量值随之改变。
个人理解:先在state中定义变再量把值绑定到输入框上,再通过事件、事件对象把值取回来存放到state中【双向绑定实现原理】
普通输入框
1 2 3 4 5 6 7 8 9 10 11 12 13
| state = { username:'', }
handleA = (e) => { this.setState({ username: e.target.value }) }
<p>{ this.state.username }</p> <input type="text" value={ this.state.username } onInput={ this.handleA } placeholder='请输入用户名'/>
|
文本域textarea
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| state = { textField: '', }
handleB = (e) => { this.setState({ textField: e.target.value }) }
{} <p>{this.state.textField}</p> <textarea value={this.state.textField} onInput={this.handleB}></textarea>
|
下拉框【它的值在select身上,同时一般结合映射对象,value值会使用1,2,3,通过对象中括号语法映射成需要显示的 字符串】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| state = { selectVal: '', }
handleC = (e) => { this.setState({ selectVal: e.target.value }) }
{} <select value={this.state.selectVal} onChange={this.handleC}> <option value="">全部</option> <option value="上海">上海</option> <option value="北京">北京</option> <option value="深圳">深圳</option> </select>
|
单选按钮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| state = { radioVal: 'male', }
handleD = (e) => { this.setState({ radioVal: e.target.value }) }
{} <p>{this.state.radioVal}</p> <input type="radio" value="male" onChange={this.handleD} id="male" checked={this.state.radioVal === 'male'}/> {} <label htmlFor="male"></label> <input type="radio" value="female" onChange={this.handleD} id="female" checked={this.state.radioVal === 'female'}/> <label htmlFor="female"></label>
|
复选框【单个使用】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| state = { checkVal: false, }
handleE = () => { this.setState({ checkVal: !this.state.checkVal }) }
{} {} <p>{JSON.stringify(this.state.checkVal)}</p> <input type="checkbox" onChange={this.handleE} checked={this.state.checkVal} />大武汉
|
复选框【多个使用】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| state = { checkArr: [], }
handleF = (e) => { var arr = JSON.parse(JSON.stringify(this.state.checkArr)) var val = e.target.value var index = arr.findIndex(item => item===val) if (index === -1) { arr.push(val) } else { arr.splice(index, 1) } this.setState({ checkArr: arr }) }
{} <p>{JSON.stringify(this.state.checkArr)}</p> <input type="checkbox" value="Wuhan" onChange={this.handleF} checked={this.state.checkArr.includes('Wuhan')} />大武汉 <input type="checkbox" value="Beijing" onChange={this.handleF} checked={this.state.checkArr.includes('Beijing')} />老北京 <input type="checkbox" value="Shanghai" onChange={this.handleF} checked={this.state.checkArr.includes('Shanghai')} />大上海
|
eg1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| import React, { Component } from 'react'
class ModelDemo extends Component { state = { username: '', textField: '', selectVal: '', radioVal: 'male', checkVal: false, checkArr: [], } handleA = (e) => { this.setState({ username: e.target.value }) } handleB = (e) => { var arr = JSON.parse(JSON.stringify(this.state.checkArr)) var val = e.target.value var index = arr.findIndex(item => item === val) if (index === -1) { arr.push(val) } else { arr.splice(index, 1) } this.setState({ checkArr: arr }) } handleC = (e) => { this.setState({ selectVal: e.target.value }) } handleD = (e) => { this.setState({ radioVal: e.target.value }) } handleE = () => { this.setState({ checkVal: !this.state.checkVal }) } handleF = (e) => { var arr = JSON.parse(JSON.stringify(this.state.checkArr)) var val = e.target.value var index = arr.findIndex(item => item===val) if (index === -1) { arr.push(val) } else { arr.splice(index, 1) } this.setState({ checkArr: arr }) } render() { return ( <div> <h3>受控组件</h3> {/* 普通输入框 */} <p>{this.state.username}</p> <input value={this.state.username} onInput={this.handleA} type="text" placeholder='请输入用户名' /> {/* 文本域 */} <p>{this.state.textField}</p> <textarea value={this.state.textField} onInput={this.handleB}></textarea> {/* 下拉框【它的值在select身上,同时一般会结合映射对象(自行创建好这个映射对象),value值会使用1,2,3这种,而表单中的各元素取值都是字符串类型,再通过对象中括号语法映射成需要显示的字符串】 */} <p>{this.state.selectVal}</p> <select value={this.state.selectVal} onChange={this.handleC}> <option value="">全部</option> <option value="上海">上海</option> <option value="北京">北京</option> <option value="深圳">深圳</option> </select> {/* 单选按钮 */} <p>{this.state.radioVal}</p> <input type="radio" value="male" onChange={this.handleD} id="male" checked={this.state.radioVal === 'male'} /> {/* react中用的htmlFor来实现label的绑定,vue中使用的是for这个属性 */} <label htmlFor="male"></label> <input type="radio" value="female" onChange={this.handleD} id="female" checked={this.state.radioVal === 'female'} /> <label htmlFor="female"></label> {/* 复选框checkbox */} {/* 单个使用,不绑定value,通过布尔值取反决定选中状态 */} <p>{JSON.stringify(this.state.checkVal)}</p> <input type="checkbox" onChange={this.handleE} checked={this.state.checkVal} />大武汉 {/* 多个使用,绑定value值,通过判断数组是否包含决定选中状态 */} <p>{JSON.stringify(this.state.checkArr)}</p> <input type="checkbox" value="Wuhan" onChange={this.handleF} checked={this.state.checkArr.includes('Wuhan')} />大武汉 <input type="checkbox" value="Beijing" onChange={this.handleF} checked={this.state.checkArr.includes('Beijing')} />老北京 <input type="checkbox" value="Shanghai" onChange={this.handleF} checked={this.state.checkArr.includes('Shanghai')} />大上海 </div> ) } }
export default ModelDemo
|
非受控组件,ref的使用
react中ref 不止获取dom元素,也可以获取子组件的实例
- 获取dom元素进而获取元素的值,通过ref内联方式快速获取
ref内联方式,ref传入的实参username,是对该元素的标记===能获取该元素,挂到组件的username属性上,这样就能直接通过this.username获取到该元素
1 2 3 4 5 6 7
| handleA = () => { console.log('获取用户名', this.username.value) }
<input ref={username => this.username = username} type="text" placeholder='请输入用户名' /> <button onClick={this.handleA}>登录A</button>
|
- 获取dom元素进而获取元素的值,引入
createRef
,通过构造器定义一个ref,再去模板上给对应元素绑定ref属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import React, { Component, createRef } from 'react'
constructor(props) { super(props) this.pwd = createRef() }
handleB = () => { console.log('获取密码', this.pwd.current.value) }
{} <input ref={this.pwd} type="pwd" placeholder='请输入密码'/> <button onClick={this.handleB}>登录B</button>
|
- 获取子组件里面的数据即state中的值,引入
createRef
,通过构造器定义一个ref,再去模板上给子组件占位标签绑定ref属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
import React, { Component, createRef } from 'react'
constructor(props) { super(props) this.child = createRef() }
handleC = () => { console.log('获取子组件中的state', this.child.current.state) console.log('获取子组件中的state里面的值', this.child.current.state.num) }
{} <Child ref={this.child} /> <button onClick={this.handleC}>登录C</button>
class Child extends Component { state = { num: 100 } render() { return ( <div> Child </div> ) } }
export default Child
|
React的生命周期
生命周期:
- 挂载阶段:constructor getDerivedStateFromProps render componentDidMount(常用)
- 更新阶段:getDerivedStateFromProps shouldComponentUpdate render getSnapshotBeforeUpdate
componentDidUpdate更新后
- 销毁阶段: componentWillUnmount销毁前
- 废弃生命周期: componentWillMount 挂载前 componentWillReceiveProps props改变就执行
componentWillUpdate更新前
挂载阶段,挂载前通过constructor继承父类,获取props和state
1 2 3 4 5 6 7
| constructor(props) { super(props) this.state = { num: 0 } console.log('constructor初始化组件时候执行一次,继承父类,获取props和state') }
|
挂载阶段,挂载前的render,把jsx模板转化成vdom,多次调用
1 2 3
| render() { console.log('render 把jsx模板转化成vdom,多次调用') }
|
挂载阶段:挂载后,dom节点渲染完毕再执行
1 2 3
| componentDidMount() { console.log('componentDidMount 挂载后,dom节点渲染完毕在执行') }
|
更新阶段,同步父组件props到当前组件中的state
nextProps是最新的props,prevState是上一个状态的state
1 2 3 4
| static getDerivedStateFromProps(nextProps, prevState) { console.log('getDerivedStateFromProps 同步父组件props到当前组件中的state') return null }
|
更新阶段,shouldComponentUpdate简称SCU做性能优化,return true标识render会执行,false标识render不执行
1 2 3 4
| shouldComponentUpdate(){ console.log('shouldComponentUpdate 做性能优化,return true标识render会执行,false标识render不执行'); return true }
|
更新阶段,修改更新后
1 2 3
| componentDidUpdate() { console.log('componentDidUpdate 修改后') }
|
销毁阶段,销毁前
1 2 3
| componentWillUnmount() { console.log('componentWillUnmount 销毁前') }
|
数据发生改变,更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| handleClick = ()=>{ this.setState({num:this.state.num+1}) }
render() { console.log('render 把jsx模板转化成vdom,多次调用'); return ( <div> <h3>生命周期</h3> <p>{ this.state.num }</p> <button onClick={ this.handleClick }>+1</button> </div> ) }
|
父子通信
父往子通信
父组件的数据动态属性绑定在子组件占位符,子组件通过props获取即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| state = { arr: [ { id: 1, username: '小明' }, { id: 2, username: '小洪' }, { id: 3, username: '小李' } ] }
render() { return ( <div> <h3>父组件</h3> <Child arr={this.state.arr} /> </div> ) }
render() { return ( <div> <h3>子组件</h3> { this.props.arr.map(item => <li key={item.id}>{item.username}</li>) } </div> ) }
|
子往父通信
父组件把某个函数动态属性绑定到子组件的占位符,子组件通过props回调这个函数并传参即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| addHandle = (val) => { console.log('触发了', val) this.setState({ arr: this.state.arr.concat({ id: new Date().getTime(), username: val }) }) }
<Child arr={this.state.arr} addHandle={this.addHandle} />
<input onKeyDown={this.handleClick} ref={username => this.username=username} type="text" placeholder='请输入用户名'/> { this.props.arr.map(item => <li key={item.id}>{item.username}</li>) }
handleClick = (e) => { if (e.keyCode === 13) { var val = this.username.value this.props.addHandle(val) } }
|
跨层级通信
在实际的项目中,当需要组件间跨级访问信息时,如果还使用组件层层传递props,此时代码显得不那么优雅,甚至有些冗余。在react中,我们还可以使用context来实现跨级父子组件间的通信。
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据。
在React的Context中,数据我们可当成商品,发布数据的组件会用provider身份(卖方),接收数据的组件使用consumer身份(卖方)
步骤:
- 定义全局context,全局数据源
1 2 3 4 5 6 7
|
import React from 'react' export default React.createContext('1000')
|
方案一:适用类组件和函数组件
引入全局context到提供商品的组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
import Child1 from './Child1' import context from "../context"
let { Provider } = context
class ContextDemo extends Component { state = { num: 100, arr: [1,2,3,4,5] } render() { return ( <div> <h3>跨层级通信</h3> {/* provider是用来给后代传递数据的,value就是传递的数据 */} <Provider value={this.state.num}> // 不能直接传递state {/* 子组件1 */} <Child1 /> </Provider> </div> ) } }
class Child1 extends Component { render() { return ( <div> <h3>Child1</h3> {/* 子组件2 */} <Child2 /> </div> ) } }
import context from "../context "
let { Consumer } = context class Child2 extends Component { render() { return ( <div> <h3>Child2</h3> <Consumer> { value => { return <div>获取到的数据:{value}</div> } } </Consumer>
</div> ) } }
|
方案二,只适用于类组件,通过this.context 拿到我们的值,可以在任意地方使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import Child1 from './Child1'
import context from "../context" let { Provider } = context
class ContextDemo extends Component { state = { num: 100, arr: [1,2,3,4,5] } render() { return ( <div> <h3>跨层级通信</h3> {/* Provider是用来给后代传递数据的,value就是传递的数据 */} <Provider value={this.state}> {/* 子组件1 */} <Child1 /> </Provider> </div> ) } }
import React, { Component } from 'react' import context from "../context"
class Child2 extends Component { static contextType = context render() { return ( <div> <h3>Child2</h3> {/* 方案2 */} <p>{this.context.arr}</p> </div> ) } }
|
- 两者区别:方案一与方案二如果祖先提供this.state,那么方案一需要value.arr取值,方案二直接this.context就是arr;如果祖先提供this.state.arr,那么方案一value直接就是arr,方案二需要this.context.arr取值
Bus通信
bus通信:任何两个毫无相关的组件都可以使用bus通信来进行传递。
步骤:
安装插件npm i -S events
,创建一个eventBus文件,定义全局事件中心并抛出
1 2 3
| import { EventEmitter } from 'events' export default new EventEmitter()
|
发送自定义事件的组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import eventBus from '../eventBus' class Child1 extends Component { state = { num: 100 } handle = () => { eventBus.emit('handleClick', this.state.num) } render() { return ( <div> <h3>Child1</h3> <button onClick={this.handle}>点击</button> </div> ) } }
|
- 添加自定义事件的组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import eventBus from '../eventBus' class Child2 extends Component { componentDidMount() { eventBus.addListener('handleClick', (val) => { console.log('触发了', val) }) } render() { return ( <div> <h3>Child2</h3> </div> ) } }
|