【react】类式组件三大核心属性

1、state

state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
组件被称为"状态机",通过更新组件的state来更新对应的页面显示(重新渲染组件)
用state来初始化状态,用setState这个API来对状态进行更改
最原始的写法:
对于类来说,状态一般是维护在constructor函数中的,对于继承的类来说,constructor函数可有可无,有初始化状态的操作就要写,如果写了就一定要调用super方法

<script type="text/babel">
class Weather extends React.Component {
	// 构造器中的this本身就是该类的实例对象
	constructor(props) {
	   // 构造器调用几次 —— 1次
	   super(props)
	   // 初始化状态
	   this.state = {isHot:true}
	   // 解决changeWeather中this指向问题
	   this.changeWeather = this.changeWeather.bind(this)
	 }
	 // 写了组件标签,react内部执行了const w = new Weather(),通过w.render()去调用的,所以render中的this也是该类的实例对象
	 render() {
	   // render调用几次 —— 1+n次 1是初始化的时候调用,n是状态更新的次数 
	   // 读取状态
	   const {isHot} = this.state
	   return <h2 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
	 }
	 changeWeather() {
	   // changeWeather调用几次 —— 点击几次调几次
	   // changeWeather放在Weather的原型对象上,供实例使用
	   // 由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
	   // 类中方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
	
	   // 获取原来的isHot值
	   const isHot = this.state.isHot
	   // 严重注意:state不可直接更改,必须通过setState进行更新,且更新是一种合并不是替换
	   // this.state.isHot = !isHot (错误写法,这就是直接更改)
	   this.setState({isHot:!isHot})
	 }
	}
	ReactDOM.render(<Weather/>,document.getElementById('test'))
</script>

简写:

  <script type="text/babel">
    class Weather extends React.Component {
      // 类中可以直接写赋值语句,含义是给类的实例对象添加一个属性
      // 类中写方法是在类的原型对象上添加
      state = {isHot:true,wind:'微风'}
      render() {
        const {isHot,wind} = this.state
        return <h2 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h2>
      }
      // 自定义方法——要用赋值语句的形式+箭头函数
      // 箭头函数没有自己的this,如果在里面写了this关键字,它会找其外层函数的this作为箭头函数的this去使用
      changeWeather = () => {
        // this指向Weather的实例对象
        console.log(this)
        const isHot = this.state.isHot
        this.setState({isHot:!isHot})
      }
    }
  </script>

强烈注意点:

  • 组件render方法中this为组件实例对象
  • 组件自定义的方法中this为undefined,如果解决?
    • 强制绑定this,通过函数对象的bind
    • 箭头函数
  • 状态数据,不能直接修改或更新

2、props

每个组件对象都会有props属性,组件标签的所有属性都保存在props中
通过标签属性从组件外向组件内传递变化的数据,注意:组件内部不要修改props数据
1、基本使用

ReactDOM.render(<Person name="tom" age="18" sex="男" />, 

2、批量传递

在react里面允许用扩展运算符展开对象

注意:仅用于标签属性的传递,其他地方不行

 const p = {name:'老刘',age:18,sex:'男'}
 // ...p用来展开p对象,{}是jsx语法,要区别于复制对象
 ReactDOM.render(<Person {...p} />,document.getElementById('test2'))

扩展:原生js扩展运算符不能展开对象,可以克隆与合并对象

const p = {name:'老刘',age:18,sex:'男'}
console.log(...p); 

在这里插入图片描述

var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };
var clonedObj = { ...obj1 };
// 克隆后的对象:{ foo: "bar", x: 42 }
var mergedObj = { ...obj1, ...obj2 };
// 合并后的对象:{ foo: "baz", x: 42, y: 13 }

【react】类式组件三大核心属性_第1张图片

3、对props进行限制

// 对标签属性进行类型、必要性的限制
Person.propTypes = {
  name:PropTypes.string.isRequired, // 限制name必传且为字符串
  sex:PropTypes.string,
  age:PropTypes.number,
  speak:PropTypes.func, // 限制speak为函数
}
// 指定默认标签属性值
Person.defaultProps = {
  sex:"男",
  age:18
}

扩展:在类的外面给类添加属性用类.属性 =

4、props的简写方式

<script type="text/babel">
class Person extends React.Component {
   constructor(props) {
     // 构造器是否接收props,是否传递给super,取决于是否希望在构造器中通过this访问props
     super(props)
   }
   // 对标签属性进行类型、必要性的限制
   static propTypes = {
     name: PropTypes.string.isRequired, // 限制name必传且为字符串
     sex: PropTypes.string,
     age: PropTypes.number,
   }
   // 指定默认标签属性值
   static defaultProps = {
     sex: "男",
     age: 18
   }
   render() {
     const { name, age, sex } = this.props
     return (
       <ul>
         <li>姓名:{name}</li>
         <li>性别:{sex}</li>
         <li>年龄:{age + 1}</li>
       </ul>
     )
   }
 }

扩展:在类的里面给类添加属性用static 属性 =

5、函数式组件使用props

因为函数能够接收参数

<script type="text/babel">
	//创建组件
	function Person (props){
		const {name,age,sex} = props
		return (
				<ul>
					<li>姓名:{name}</li>
					<li>性别:{sex}</li>
					<li>年龄:{age}</li>
				</ul>
			)
	}
	Person.propTypes = {
		name:PropTypes.string.isRequired, //限制name必传,且为字符串
		sex:PropTypes.string,//限制sex为字符串
		age:PropTypes.number,//限制age为数值
	}

	//指定默认标签属性值
	Person.defaultProps = {
		sex:'男',//sex默认值为男
		age:18 //age默认值为18
	}
	//渲染组件到页面
	ReactDOM.render(<Person name="jerry"/>,document.getElementById('test'))
</script>

3、refs

1、字符串形式的ref

官方推荐尽量避免使用(效率不高)

<input ref="input1" type="text" placeholder="点击按钮提示数据" />

通过this.refs.input1可以拿到这个input节点(真实DOM)

2、回调函数形式的ref

回调函数内联写法存在调用次数的问题,也可以把回调函数写出来,但是内联写法存在的问题基本可以忽略,开发中也常用

当前节点放在类实例的自身上

// 内联的回调
<input ref={c => this.input1 = c} type="text" placeholder="点击按钮提示数据" />

ref = { c => {} },react会自动调用并且把ref当前所处的节点作为实参传进去

回调ref中回调执行次数的问题

初次渲染会自动调用回调函数,把当前节点传进去;当状态更改,重新调用render函数,发现有ref并且是回调函数的形式,且是一个新的函数(之前的函数执行完后被释放)。因为不确定之前的函数做了一些什么动作,为了保证之前传的节点被清空,所以第一次调用传了null,紧接着调用第二次把当前节点放进去。

官网描述:

如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。

回调方法放在类实例的自身上

// 内绑定的回调
<input ref={this.saveInput} type="text" placeholder="点击按钮提示数据" />

重新调用render函数,但实例自身上有saveInput之前调过,不再是新的函数了

saveInput = (c)=>{
	this.input1 = c;
	console.log('@',c);
}

3、createRef

React.createRef()调用后可以返回一个容器,该容器可以存储被ref所标识的节点,但该容器是 “专人专用” 的

<script type="text/babel">
	class Demo extends React.Component{
		myRef = React.createRef()
		myRef2 = React.createRef()
		//展示左侧输入框的数据
		showData = ()=>{
			alert(this.myRef.current.value);
		}
		//展示右侧输入框的数据
		showData2 = ()=>{
			alert(this.myRef2.current.value);
		}
		render(){
			return(
				<div>
					<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
					<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
					<input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
				</div>
			)
		}
	}
	ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
</script>

你可能感兴趣的:(react,react.js)