react学习笔记(4) - ref属性以及状态初体验

ref属性

  • 在我看来,react中的ref属性与Vue中的ref属性是十分的相似的,两者的功能斗鱼原生js中的id选择器非常的相似,都是用于定位一个目标(容器)使我们可以通过这个ref属性去操作这个目标,在组件内的标签可以定义ref属性来标识自己(Vue,React)

使用方法:

  1. 老版本使用(不推荐使用,后续会被淘汰)
    • 1. 给标签设置ref="username"
      • 通过这个获取this.refs.username , ref可以获取到应用的真实dom
    • 2. 给组件设置ref="username"
      • 通过这个获取this.refs.username ,ref可以获取到组件对象
import React, { Component } from 'react'
export default class App extends Component {
render() {
return (
<div>
{/* 老版本设置ref属性(不推荐使用) */}
<input ref="myText"/>

{/* ----------------- ref属性 --------------------- */}

{/* 事件绑定一:直接在事件后面跟上函数体, 在逻辑体量较小的情况下推荐使用 */}
<button onClick={() => {//其他事件也是一样的,onMouseOver,类似Vue中的@click
//使用this.refs来获取对应的ref指向容器,.value输出里面的内容
console.log('按钮点击了!',this.refs.myText.value)
}}>add</button>
</div>
)
}
}
  • 结果展示:
    image
  1. 新版本的使用(推荐使用)
    • 切记在使用事件绑定的时候理清楚函数中的this指向,推荐使用箭头函数!
import React, { Component } from 'react'
export default class App extends Component {

// 1. 首先使用createRef来创建对应的Ref变量
myText = React.createRef()//调用这个方法返回的是一个Ref对象
render() {
return (
<div>
{/* 新版本设置ref属性(推荐使用) */}
<input ref={this.myText}/>

{/* ----------------- ref属性 --------------------- */}

{/* 事件绑定一:直接在事件后面跟上函数体, 在逻辑体量较小的情况下推荐使用 */}
<button onClick={() => {//其他事件也是一样的,onMouseOver,类似Vue中的@click
//使用this.refname(对应的ref变量名)来货期对应的ref指向容器
console.log('按钮点击了!',this.myText.current.value)//里面的current指向的是对应的ref容器(dom)
}}>add</button>
</div>
)
}
}
  • 结果展示:
    image

状态(state)

  • 状态就是组件描述某种显示情况的数据,由组件自己设置和更改,也就是说由组件自己维护,使用状态 的目的就是为了在不同的状态下使组件的显示不同(自己管理),可以对比Vue中的data,也就是响应式数据,每一次修改状态,页面自动修改, 这就说明相较于Vue中的data, React中是使用state来保存数据(状态)的

定义状态(数据)state的两种方法

  1. 方法一(直接定义state)
import React, { Component } from 'react'
export default class state extends Component {
// 1. 设置状态(state)的第一种方法-直接定义
state = {
isShwo : true,
username:'lam'
}
render() {
return (
<div>
<h1>你好! React-{this.state.username}</h1>
<button onClick={()=>{
// 2. 修改状态(不能直接赋值修改,一定要使用setState来修改)
this.setState({
isShwo:!this.state.isShwo,
username:'肥林'
})
}}>{this.state.isShwo?'收藏':'取消收藏'}</button>
</div>
)
}
}
  1. 方法二(使用构造函数定义)
import React, { Component } from 'react'
export default class state extends Component {
// 2. 设置状态(state)的第一种方法-使用构造函数
constructor(){
super()//切记一定要使用super来继承
this.state = {
isShwo : true,
username:'lam'
}
}
render() {
return (
<div>
<h1>你好! React-{this.state.username}</h1>
<button onClick={()=>{
// 2. 修改状态(不能直接赋值修改,一定要使用setState来修改)
this.setState({
isShwo:!this.state.isShwo,
username:'肥林'
})
}}>{this.state.isShwo?'收藏':'取消收藏'}</button>
</div>
)
}
}

结果展示

image

解析:

  • this.state是纯js对象,在vue中,data属性是利用object.defineProperty处理过的,更改data的数据的时候会触发数据的gettersetter的,也就是说我们在Vue中可以直接修改data中的数据状态,但是React中没有做过这样的处理,如果直接更改的话React是无法直接得知的,因此我们需要使用React中提供的修改数据状态的方法setState()来修改state中的数据

列表渲染

  • React中是不提供类似Vue中的for in语法的,也就是锁我们只能使用原生的js语法来实现循环渲染数组或对象,这里推荐使用Es6中的map方法

  • 代码如下:(读取一个数组中的数据并以ul-li标签的组合形式渲染到页面上)

import React, { Component } from 'react'
export default class App extends Component {
state={//定义状态
list:['张三','李四','王五','老六']
}
render() {
return (
<div>
<h1>列表渲染(map循环)</h1>
<ul>
{/* 使用{}包裹js代码[jsx语法] */}
{
this.state.list.map((item,index)=>{
console.log('对应的索引值',index);//输出指定的索引值(相当于对应的id)唯一的标识
return <li key={index}>{item}</li>
})
}
</ul>
</div>
)
}
}

key值解析(与Vue中的key值基本一致的认知即可)

  • React的高效依赖于所谓的 Virtual-DOM(虚拟Dom),尽量不碰 DOM。对于列表元素来说会有一个问题:元素可能会在一个列表中改变位置。要实现这个操作,只需要交换一下 DOM 位置就行了,但是React并不知道 其实我们只是改变了元素的位置,所以它会重新渲染后面两个元素(再执行 Virtual-DOM ),这样会大大增加 DOM 操作。但如果给每个元素加上唯一的标识(索引值),React 就可以知道这两个元素只是交换了位置,这个标识就是key值,且这个key值必须是元素的唯一标识。(其实就跟Vue中的理解基本一致就行了)

结果展示

image

条件渲染(对比Vue中的v-if,v-else等...)

  • React中的条件渲染还是使用原生的js实现的,基本有以下三种方法:
{/* 条件渲染的两种方式,还是原生的js */}
{/* 1. 三目运算符方法 */}
{this.state.list.length === 0?<div>暂无待办事项!</div>:null}
{/* 2. 与门运算符 */}
{this.state.list.length === 0 && <div>暂无待办事项!</div>}
{/* 3. 引入外联样式 */}
<div className={this.state.list.length === 0?null:'isShow'}>暂无待办事项</div>

React中的富文本编辑展示(dangeroutlySetInnerHTML)

  • 类似于Vue中的v-html,用于使页面解析html代码
  • 处于安全的原因,React当中所有表达式的内容会被转义,如果直接输入,标签会被当成文本。这时候 就需要使用dangeroutlySetInnerHTML属性,它允许我们动态的设置innerHTML
import React, { Component } from 'react'

export default class App extends Component {
state = {
content : "<strong>你好! React</strong>"//html内容
}
render() {
return (
// 使用dangerouslySetInnerHTML来渲染html内容
<div dangerouslySetInnerHTML={
{
// 注意这里是两个下下划线 __html
__html: this.state.content // 用来指定渲染内容
}
}></div>
)
}
}

结果展示:

image

setState()方法解析

  • 我们都知道,在React中,想要修改state中的状态就必须调用setState()进行修改,且数据的更新是一种合并,不是替换,也就是说,我们可以直接使用setState()来修改state中的状态(数据),但是却不会对未修改的状态进行任何的操作,且每一次调用setState()来修改状态,页面都会重新渲染一次,也就是render的调用次数。

image

  • 由此图可以看见,我们1每一次更改state中的list状态,flag状态都是不变的,这也就说明了,状态的更新是一种合并操作。

  • setState()接受两个参数,第一个参数就是一个对象,用于指定修改state中的状态操作,第二个参数是回调函数,这个回调函数是在使用setState()修改完状态后,页面重新渲染完成(DOM重更新完成)的时候调用的,

import React, { Component } from 'react'

export default class App extends Component {
state = {
count:1,//初始数为1
}
render() {
return (
<div>
<button onClick={()=>{
// 每次点击count+1
let newCount = this.state.count + 1
this.setState({count:newCount},()=>{
// setState的第二个参数,回调函数,每一次调用setState后页面重新渲染完成后调用
console.log('页面渲染完成',this.state.count);
})
}}>add</button>
</div>
)
}
}

  • 结果展示:

image

  • 旧版本(18以下)setState()还有一个特性就是,当我们调用setState()时处于同步的逻辑中,状态是异步更新的,DOM也是异步更新的,但是当我们处于异步的逻辑中去调用setState()时,状态却是同步更新的,DOM也是如此!!!但是18版本过后,多有的setState()操作无论在同步还是异步逻辑下均为异步更新状态!

小案例:todoList

  • 案例要求: 在input框中填入想要做的事随后点击add按钮将待办事项push进入state中的list状态中,并且伴随着生成一个删除按钮,点击删除按钮调用原生的js删除数组(splice)中的某一项的方法删除对应的待办事项,且每一次点击添加按钮添加待办事项后会清空输入框且每当没有待办事项的时候页面会显示当前暂无待办事项

代码展示:

import React, { Component } from 'react'
import './css/myCss.css'//引入外联样式文件
export default class App extends Component {
myText = React.createRef()//创建一个ref属性指向输入框
state = {//设置状态
list : [],//待做事项
flag:'演示标志位',
}
render() {
return (
<div>
<h1>简易版todoList</h1>

<input ref={this.myText}/>
<button onClick={()=>{
// 切记不能直接操作state中的状态,要使用新的变量来赋值状态再操作
var newList = this.state.list // 1. 做变量赋值
newList.push(this.myText.current.value) // 2. 操作变量
// 3. 再将操作后的变量再赋值回给对应的状态
this.setState({
list:newList,
})
// console.log(this.myText.current.value)
// 清空输入框
this.myText.current.value = ''
}}>add</button>
<ul>
{
this.state.list.map((item,index)=><li key={index}>{item}<button onClick={()=>{
//这里切记要使用箭头函数包裹对应的回调函数方法,否者他会直接调用删除的回调函数
this.handleDelete(index)
}}>删除</button></li>)
}
</ul>

{/* 条件渲染的三种方式,还是原生的js */}
{/* 1. 三目运算符方法 */}
{this.state.list.length === 0?<div>暂无待办事项!</div>:null}
{/* 2. 与门运算符 */}
{this.state.list.length === 0 && <div>暂无待办事项!</div>}
{/* 3. 样式 */}
<div className={this.state.list.length === 0?null:'isShow'}>暂无待办事项</div>
</div>
)
}

handleDelete(index){//删除方法
console.log('要删除的对应item的id为',index);

// 删除的逻辑
// 1. 做变量赋值
let newList = this.state.list
// 2. 操作变量
newList.splice(index,1)//删除对应index中的1个
// 3. 再将操作后的变量再赋值回给对应的状态
this.setState({
list:newList
})
}
}

结果展示:

image