属性vs状态
相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)
不同点:
1. 属性能从父组件获取,状态不能
2. 属性可以由父组件修改,状态不能
3. 属性能在内部设置默认值,状态也可以
4. 属性不在组件内部修改,状态要改 【 属性只能外部修改,内部不允许修改】
5. 属性能设置子组件初始值,状态不可以
6. 属性可以修改子组件的值,状态不可以
state
state的主要作用是用于组件保存、控制、修改自己的可变状态
state在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。
你可以认为 state是一个局部的、只能被组件自身控制的数据源。
state中状态可以通过 this.setState方法进行更新,setState会导致组件的重新渲染。
props
props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。
它是外部传进来的配置参数,组件内部无法控制也无法修改。
除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。
如果搞不清 `state` 和 `props` 的使用场景,记住一个简单的规则:尽量少地用 `state`,多用 `props`。
无状态组件也就是函数式组件
有状态组件就是类组件
经验:
功能复杂,我们使用类组件
功能单一,我们使用函数式组件
组件props要想变,那么久外部修改
组件state要想变,那么组件内部自身通过setState修改
react性能优化一个方案: 就是多使用无状态组件( 函数式组件 )
受控组件与非受控组件
React组件的数据渲染是否被调用 是通过 传递过来的props
完全控制,控制则为受控组件,否则非受控组件。下面案例 含条件渲染
状态提升
如果有多个组件共享一个数据,把这个数据放到共同的父级组件中来管理
列表渲染
import React, { Fragment, Component } from 'react'
class CompOne extends Component {
constructor( props ) {
super( props )
this.state = {
lists:[
{
id:1,
shop_name:'汽车'
},
{
id:2,
shop_name:'电脑'
}
]
}
}
render() {
const { lists } = this.state
return (
<Fragment>
<ul>
{
lists.map( value => <li key = { value.id }> { value.name } </li> )
}
</ul>
</Fragment>
)
}
}
为了让jsxdom里面更加简洁 改写一下
class CompOne extends Component {
constructor( props ) {
super( props )
this.state = {
lists:[
{
id:1,
shop_name:'汽车'
},
{
id:2,
shop_name:'电脑'
}
]
}
}
showlist = () => {
const { lists } = this.state
return lists.map( value => <li key = { value.id }> { value.name } </li> )
}
render() {
return (
<Fragment>
<ul>
{ this.showlist() }
</ul>
</Fragment>
)
}
}
export default CompOne
事件处理函数有四种
采用on+事件名的方式来绑定一个事件,注意,这里和原生的事件是有区别的,原生的事件全是小写onclick
, React里的事件是驼峰onClick
,React的事件并不是原生事件,而是合成事件。
1.直接在render里写行内的箭头函数(不推荐)
import React,{ Fragment , Component } from 'react'
class CompEve extends Component {
constructor(){
super()
this.state = {
wzz:'wzz',
cyy:'cyy',
flag:true
}
}
render(){
const { wzz , cyy , flag } = this.state
return(
<Fragment>
<button onClick = { () => { this.setState({ flag:!this.state.flag }) } }>change</button>
<div>{ (flag && wzz) || cyy }</div>
</Fragment>
)
}
}
export default CompEve
2.在组件内使用箭头函数定义一个方法(推荐) 箭头函数不会改变this指向
import React,{ Fragment , Component } from 'react'
class CompEve extends Component {
constructor(){
super()
this.state = {
wzz:'wzz',
cyy:'cyy',
flag:true
}
}
changbtn = () => {
this.setState({
flag:!this.state.flag
})
}
render(){
const { wzz , cyy , flag } = this.state
return(
<Fragment>
<button onClick = { this.changbtn }>change</button>
//用括号括起复杂的表达式可以明确开发人员的意图,从而使代码更具可读性。
//此规则会在表达式中连续使用不含运算符的不同运算符时发出警告。
<div>{ (flag && wzz) || cyy }</div>
</Fragment>
)
}
}
export default CompEve
3.直接在组件内定义一个非箭头函数的方法,然后在render里直接使用onClick={this.handleClick.bind(this)}
(不推荐)
import React,{ Fragment , Component } from 'react'
class CompEve extends Component {
constructor(){
super()
this.state = {
wzz:'wzz',
cyy:'cyy',
flag:true
}
}
changbtn(){ //3
this.setState({
flag:!this.state.flag
})
}
render(){
const { wzz , cyy , flag } = this.state
return(
<Fragment>
<button onClick = { this.changbtn.bind(this) }>change</button>
<div>{ (flag && wzz) || cyy }</div>
</Fragment>
)
}
}
export default CompEve
4.直接在组件内定义一个非箭头函数的方法,然后在constructor里bind(this)(推荐)
import React, { Fragment, Component } from 'react'
class CompEve extends Component {
constructor() {
super()
this.state = {
wzz: 'wzz',
cyy: 'cyy',
flag: true
}
this.changbtn = this.changbtn.bind(this)
}
changbtn() { //4
this.setState({
flag: !this.state.flag
})
}
render() {
const { wzz, cyy, flag } = this.state
return (
<Fragment>
<button onClick={this.changbtn}>change</button>
<div>{(flag && wzz) || cyy}</div>
</Fragment>
)
}
}
export default CompEve
事件对象Event(下面有例子:通过event传参)
和普通浏览器一样,事件handler会被自动传入一个 event
对象,这个对象和普通的浏览器 event
对象所包含的方法和属性都基本一致。不同的是 React中的 event
对象并不是浏览器提供的,而是它自己内部所构建的。它同样具有event.stopPropagation
、event.preventDefault
这种常用的方法
事件对象中的值很多都是null,但是可以正常使用
事件的参数传递
- 在
render
里调用方法的地方外面包一层箭头函数
import React,{ Fragment , Component } from 'react'
class CompEvent extends Component {
constructor( props ){
super( props )
this.state = {
money:100
}
}
chang = (val) => {
this.setState({
money:val
})
}
render(){
const { money } = this.state
return (
<Fragment> //传参需要加(),加()为调用函数,点击也会执行函数,以后加一层箭头函数点击的时候触发箭头函数
<button onClick = { () => { this.chang(1000) }}>
change
</button>
<p>money有:{ money }</p>
</Fragment>
)
}
}
export default CompEvent
- 在
render
里通过this.handleEvent.bind(this, 参数)
这样的方式来传递
import React,{ Fragment , Component } from 'react'
class CompEvent extends Component {
constructor( props ){
super( props )
this.state = {
money:100
}
}
chang(val){
this.setState({
money:val
})
}
render(){
const { money } = this.state
return (
<Fragment>
<button onClick = { this.chang.bind(this,1000) }>
change
</button>
<p>money有:{ money }</p>
</Fragment>
)
}
}
- 通过
event
传递
import React, { Fragment, Component } from 'react'
class CompName extends Component {
constructor(props) {
super(props)
this.state = {
fname: '',
lname: '',
}
}
add = (e) => { //e表示事件对象
this.setState({ //e。target表input标签
[e.target.name] : e.target.value
})
}
render() {
return (
<Fragment>
<input type="text" name="fname" onChange={ this.add } />
<p></p>
<input type="text" name="lname" onChange={ this.add } />
<p>{ this.state.fname } { this.state.lname }</p>
</Fragment>
)
}
}
export default CompName
- 比较推荐的是做一个子组件, 在父组件中定义方法,通过
props
传递到子组件中,然后在子组件件通过this.props.method
来调用
ref
import React,{ Component , Fragment } from 'react'
import Son from './son'
class CompRef extends Component {
show = () => {
console.log('ref',this)
this.refs.ele.style.backgroundColor = 'red'
this.item.style.backgroundColor = 'red'
}
render(){
return(
<Fragment>
<button onClick = { this.show }>+++</button>
<p ref = 'ele' >111</p>
<Son ref = 'comp'/>
{/* ref还可以是函数 */}
<p ref = { el => this.item = el }>ref函数形式</p>//这里将el p标签传给了item
</Fragment>
)
}
}
export default CompRef