思考一个问题:
之前我们讲到过,使用props传递的值不能动态修改。如果我们想要实现数据动态更新,该怎么办呢?或者更详细点说,如何根据用户操作、网络响应、或者其他状态变化,使组件动态的响应并改变组件的输出呢。
这个时候引入react的state状态机概念。
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
摘自菜鸟教程:https://www.runoob.com/react/react-state.html
组件被称之为"状态机",视图与状态一一对应。
在react中开发者只需要关心数据,数据改变页面就会发生改变。数据等同于状态。当状态改变时,页面绑定的数据就由react进行改变。
使用状态必须先初始化,不过初始化的方式不止一种,视具体场景变化而不同。https://blog.csdn.net/weixin_43297321/article/details/108167262
不过这次我们在类组件的constructor中完成state的初始化,语法:this.state={}
由ES6的继承规则可知,不管子类写不写constructor,在new实例的过程都会给补上constructor。
" 可以不写constructor,一旦写了constructor,就必须在此函数中写super(),super调用父类的构造方法,此时组件才有自己的this,可以在组件的全局中使用this关键字。否则如果只是constructor而不执行super() 那么以后的this都是错的!!!super()继承父组件的 this 。"
当想在constructor中使用this.props的时候,super需要加入(props)。
此时用props也行,用this.props也行,他俩都是一个东西。(不过props可以是任意参数,this.props是固定写法)
我说:反正,在使用state的时候,就把constructor和super加上。
读取状态:this.state.key
组件界面更新:this.setState({},()=>{})
小知识:setState()直接使用是异步的,会自动触发render函数的重新渲染,而且既然是异步的,就应该有回调函数,可以把要跟着这个函数一起处理的逻辑写在回调函数里;不过如果是在setTimeout或者DOM事件中使用,那么setState()是同步的。
setState是同步还是异步:https://www.kancloud.cn/freya001/haoke/1687601
简书:Component.setState()详解
简书:关于this.setState()的那些事
实现的内容:
<div id="demoSky">div>
<script type="text/babel">
class Fu extends React.Component{
constructor(props){
super(props);
this.state={
html:"I'm a target.
",
inputVal:""
}
}
fun=(e)=>{
console.log(e.target.value);
this.setState({
inputVal:e.target.value
},()=>{})
}
render(){
return(
<div>
<h4>输入的是:{this.state.inputVal}</h4>
<input type="text" onInput={this.fun}/>
{/* state渲染html,双底杠 __*/}
<p dangerouslySetInnerHTML={{__html:this.state.html}}></p>
</div>
)
}
}
ReactDOM.render(<Fu/>,document.querySelector("#demoSky"))
script>
如果我们想解析一个字符串类型的标签,如何?
直接打印是下面这个样子的:
可以使用react给我们提供的方法dangerouslySetInnerHTML = {{ __html:你要插入的字符串 }}
在有状态的前提下,不能使用工厂方式创建组件。
怎么通过工厂模式创建组件:https://blog.csdn.net/teamlet/article/details/78797386
受控组件就是可以被 react 状态控制的组件
在 react 中,Input textarea 等组件默认是非受控组件(输入框内部的值是用户控制,和React无关)。但是也可以转化成受控组件,就是通过 onChange 事件获取当前输入内容,将当前输入内容作为 value 传入,此时就成为受控组件。
好处:可以通过 onChange 事件控制用户输入,使用正则表达式过滤不合理输入。
链接:https://www.jianshu.com/p/c4fb11f42252
然后看这篇:react中的受控和非受控组件https://www.cnblogs.com/wasbg/p/11142074.html
React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。
这个特殊的属性允许你引用 render() 返回的相应的支撑实例( backing instance )。这样就可以确保在任何时间总是拿到正确的实例。
简书:https://www.jianshu.com/p/64a9cfdf72ca
React提供的这个ref属性,表示为对组件真正实例的引用。其实就是ReactDOM.render()返回的组件实例。(ref属性不能在无状态组件上使用 ref 属性,因为它们没有实例)
ReactDOM.render()渲染组件时返回的是组件实例;而渲染dom元素时,返回是具体的dom节点。
一句话总结:标识组件内部的元素
这种方法是最早的ref用法。通过this.refs.xxx
来进行访问。(官方不推荐使用)
<div id="demoSky">div>
<script type="text/babel">
class Fu extends React.Component {
constructor(props) {
super(props);
this.state = {
value:""
}
}
fun=()=>{
// console.log("输入框的值:",this.refs.inputa.value)
this.setState({
value:this.refs.inputa.value
})
}
render() {
return (
<div>
<h1>ref字符串的方式</h1>
<input type="text" ref="inputa"/><button onClick={this.fun}>点击</button>
<p>{this.state.value}</p>
</div>
)
}
}
ReactDOM.render(<Fu />, document.querySelector("#demoSky"))
script>
回调函数就是在dom节点或组件上挂载函数,函数的入参是dom节点,达到的效果与字符串形式是一样的,都是获取其引用。(官方推荐)
在React 16.3版本后,使用此方法来创建ref 。将其赋值给一个变量,通过ref挂载在dom节点或组件上该ref的current属性将能拿到dom节点或组件的实例。(React16.3新提供,常用)
不要过度使用 Refs,在对逻辑进行处理的时候尽量优先考虑state。因为考虑到代码耦合度以及后期的维护。
React负责渲染表单的组件。同时仍然控制用户后续输入时所发生的变化。值是来自于state控制的,输入表单元素称为“受控组件”。
segmentfault:https://segmentfault.com/a/1190000012404114
每个组件都包含 " 生命周期方法 ",可以重写这些方法,以便于在运行过程中特定的阶段执行这些方法。
生命周期的相关方法:
菜鸟教程:https://www.runoob.com/react/react-state.html
React事件绑定属性的命名采用小驼峰式写法。
绑定函数的过程中不加()
,否则函数会立即执行。
<button onClick={this.fun}>点我</button>
React中阻止默认行为使用 preventDefault()
,是不是跟原生js里的一样。
如果遇到this绑定失效的问题,整理出四种方法。
方式1:通过bind方法进行原地绑定,从而改变this指向
方式2:通过创建箭头函数
方式3:在constructor中提前对事件进行绑定
方式4:将事件调用的写法改为箭头函数的形式
<div id="demoSky">div>
<script type="text/babel">
class Fu extends React.Component {
constructor(props) {
super(props);
this.state = {
name:"sky"
}
// 在constructor中提前绑定this
this.fun3=this.fun3.bind(this);
}
fun1(){
this.setState({
name:"bind绑定this:this.fun1.bind(this)"
})
}
fun2=()=>{
this.setState({
name:"箭头函数的方式绑定this"
})
}
fun3(){
this.setState({
name:"在constructor中提前绑定this:this.fun3=this.fun3.bind(this);"
})
}
fun4(){
this.setState({
name:"使用箭头函数的方式进行函数的调用来绑定this:()=>{this.fun4()}"
})
}
render() {
return (
<div>
<p>{this.state.name}</p>
<button onClick={this.fun1.bind(this)}>bind绑定this</button>
<button onClick={this.fun2}>箭头函数绑定this</button>
<button onClick={this.fun3}>在constructor中提前绑定this</button>
<button onClick={()=>{this.fun4()}}>使用箭头函数的方式,进行函数的调用来绑定this</button>
</div>
)
}
}
ReactDOM.render(<Fu />, document.querySelector("#demoSky"))
script>
向事件处理函数中传递参数。
方式1(推荐):通过 bind 的方式进行传递。
方式2:通过箭头函数传递。注意:使用箭头函数调用的话,事件对象必须显式的进行传递(不需要,也可以不写事件对象)。
<div id="demoSky">div>
<script type="text/babel">
class Fu extends React.Component {
constructor(props) {
super(props);
this.state = {
name:"sky"
}
}
fun(text){
// console.log(text)
this.setState({
name:text
})
}
render() {
return (
<div>
<h4>{this.state.name}</h4>
<button onClick={this.fun.bind(this,"传递给函数的实参")}>bind方式传递参数</button>
<button onClick={()=>this.fun("箭头函数传参")}>箭头函数方式传递参数</button>
</div>
)
}
}
ReactDOM.render(<Fu />, document.querySelector("#demoSky"))
script>
注意几个问题:
(1)this指向,谁最后调用它,就指向谁。
(2)使用状态机的话,一般情况下,不能使用无状态组件,因为它不能创建实例。但后期可以通过hook来实现。
(3)state是异步的,所以可以在回调函数中写一些处理逻辑,this.setState({},()=>{})