React的基本使用
# React的组件定义方式
1.函数式组件
定义函数(名字大写,后续组件名字大写才会查询自定义组件,否则是当标签查询)
function MyComponent1(){
console.log(this); //此处的this是undefined,因为经过babel的编译后,开启了严格模式。
return <h2>函数式组件</h2>
}
渲染组件到页面
ReactDOM.render(<MyComponent1/>,document.getElementById('xxx'))
执行了ReactDOM.render后:
1.React获取<MyComponent1/>标签,判断出MyComponent是用函数定义的。
2.React调用MyComponent1并获取函数的返回的虚拟DOM,随后转为真实DOM,随后渲染到页面。
注:由于处于React+babel环境,开启的严格模式。函数中无法使用this
2.类式组件
1、定义组件
class MyComponent extends React.Component{
//render是MyComponent的原型对象上,给MyComponent的实例对象用的。
render(){
console.log(this); //MyComponent的实例对象
return <h2>类式组件</h2>
}
}
2、渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
执行了ReactDOM.render后:
1.React获取<MyComponent/>标签,判断MyComponent是用类定义的。
2.React new了一个MyComponent实例对象
3.通过实例调用MyComponent原型上的render方法,并获取到了返回的虚拟DOM,转为真实DOM,渲染页面。
注:render中是可以获取到实例的this的
# React的state属性:
1、简单写法(this指向问题需要手动解决)
class MyComponent extends React.Component{
//组件被实例化几次,就调用几次构造器
constructor(props){
super(props)
this.state = {showTest:'显示',other:'测试'} //初始化状态
//构造器中的this指向实例化对象,将changeTest的this绑定实例化对象并将函数赋值给实例化的对象的changeTest属性,
所以后面调用的changeTest并不是原型上的,是实例化对象上面的属性。
this.changeTest = this.changeTest.bind(this) //解决this指向问题
}
changeTest(){
//若构造器中不做处理,那么下面的this是undefined,因为changeTest不是通过实例调用的,
而是作为点击的回调去使用,且类中的方法自动开启了严格模式。
//更新状态,不能直接获取状态并赋值,不然不会去触发render调用的!!!
this.setState({showTest:'更改了'}) //此处更新状态是一个合并的动作,不是替换
}
//render调用次数是1+n次(n是更新状态的次数,每次状态的更改都要重新更改虚拟DOM)
render(){
//这里将函数给onClick回调事件,并没有在这里调用函数,所以changeTest中的this不是实例化对象。
return <h1 onClick={this.changeTest}>
this is test state,{this.state.showTest},{this.state.other}
</h1>
}
}
2、常用方法,利用箭头函数解决this的指向问题
class MyComponent extends React.Component{
constructor(props){
super(props)
this.state = {showTest:'显示',other:'测试'} //初始化状态
}
//使用箭头函数解决this指向问题,箭头函数的this在定义的时候由同外部环境
changeTest = ()=>{
this.setState({showTest:'更改了'})
}
render(){
//这里将函数给onClick回调事件,并没有在这里调用函数,所以changeTest中的this不是实例化对象。
return <h1 onClick={this.changeTest}>this is test state,{this.state.showTest},{this.state.other}</h1>
}
}
注: 原生JS中绑定函数,是将函数绑定,react的绑定的函数,是将函数的返回值提交绑定
jsx:
<h1 onClick={this.changeTest}>this is test state,{this.state.showTest},{this.state.other}</h1>
js:事件名大小写不同,并且绑定的方式不同,
原生中不会执行changeTest()这个函数,但是jsx中加括号会执行函数,绑定返回值。
<h1 onclick='this.changeTest()'>this is test state</h1>
# React的props属性
class Person extends React.Component{
//对传给Person组件的props进行类型的限制
static propTypes = {
name:PropTypes.string, //限制name必须为字符串类型
** sex:PropTypes.string.isRequired,//限制sex必须为字符串类型,且是必要属性
age:PropTypes.number,//限制age必须为数值类型
address:PropTypes.string, //限制address必须为字符串类型
}
//对传给Person组件的props进行默认值的设置
static defaultProps = {
address:'中国'
}
render(){
const {name,age,sex,address} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
<li>地址:{address}</li>
</ul>
)
}
}
//渲染组件
ReactDOM.render(<Person name="tom" sex="女" age="18"/>,document.getElementById('XXX'))
const p1 = {
name:'程老师',
sex:'男',
age:19,
address:"北七家镇"
}
//下面的...p1,并不是原生js里的{...p1},{}是jsx的写法要求
//babel+react环境就可以让展开运算符展开一个对象,但是仅仅适用于传递标签属性!!
ReactDOM.render(<Person {...p1}/>,document.getElementById('XXX'))
//高级用法,利用prop传递一个箭头函数(用于子组件传递参数给父组件)
updateAppState = (stateObj)=>{
this.setState(stateObj)
}
render() {
return (
<div className="container">
//将更新的函数通过prop传递给子组件
<Search updateAppState={this.updateAppState}/>
</div>
)
}
//子组件中通过调用prop的函数进行传参
this.props.updateAppState({...})
# React的refs属性
1、字符串类型的Refs,标记了ref的节点可以直接通过refs属性获取。(官方不建议继续使用,论坛显示有效率问题)
class Test extends React.Component{
//箭头函数解决this指向问题
showData = ()=>{
//获取用户的输入,inputRef是真实DOM节点!!
const {inputRef} = this.refs
//提示数据
alert(inputRef.value)
}
render(){
return (
<div>
<input ref="inputRef" type="text" placeholder="点击按钮提示输入"/>
<button onClick={this.showData}>点我提示数据</button>
</div>
)
}
}
2、回调形式的ref,在ref中传入回调函数,函数的形参默认就是当前节点ref,直接绑定ref(形参)到实例的属性上。
class Demo extends React.Component{
showData = ()=>{
//获取用户的输入,input1是真实DOM节点!!
const {inputRef} = this
//提示数据
alert(inputRef.value)
}
render(){
return (
<div>
<input ref={c => this.inputRef = c} type="text" placeholder="点击按钮提示输入"/>
<button onClick={this.showData}>点我提示数据</button>
</div>
)
}
}
3、createRef创建ref,类中直接使用React.createRef()创建容器,虚拟节点上直接使用ref指向容器即可
class Demo extends React.Component{
//使用createRef,可以创建一个存储节点的容器,并且每个容器只能存储一个ref节点,多个ref需要多次创建。
container1 = React.createRef()
container2 = React.createRef()
showData = ()=>{
const {current} = this.container1
alert(current.value)
}
showData2 = ()=>{
const {current} = this.container2
alert(current.value)
}
render(){
return (
<div>
<input ref={this.container1} onBlur={this.showData1} type="text"/>
<input ref={this.container2} onBlur={this.showData2} type="text"/>
</div>
)
}
}
注:
1.类式组件中的构造器,完全可以省略掉
2.若在类式组件中写了构造器,那就必须调用super,调用super时,如果不传props,那么在构造器中,通过this.props是不可以访问props的
# React的生命周期(V16.4^)
//定义组件
class LifeHoook extends React.Component{
//构造器
constructor(){
console.log('---constructor---');
super()
this.state = {count:0}
}
//更改状态
add = ()=>{
const {count} = this.state
this.setState({count:count+1})
}
//卸载的回调
death = ()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
//强制更新的回调
force = ()=>{
this.forceUpdate()
}
//会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
//它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
static getDerivedStateFromProps(props,state){
console.log('---getDerivedStateFromProps---',props,state)
return null
}
//在最近一次渲染输出(提交到 DOM 节点)之前调用。
//它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。
//此生命周期方法的任何返回值将作为参数传递给 componentDidUpdate()。
getSnapshotBeforeUpdate(prevProps, prevState){
console.log('---getSnapshotBeforeUpdate---',prevProps, prevState)
return null
}
//组件更新完毕----调用n次,n是更新的次数,会在更新后会被立即调用。首次渲染不会执行此方法。
componentDidUpdate(prevProps, prevState, snapshot){
console.log('---componentDidUpdate----',prevProps, prevState, snapshot);
}
//组件将要挂载---调1次
UNSAFE_componentWillMount(){
console.log('---componentWillMount---');
}
//组件挂载完毕---调1次,会在组件挂载后(插入 DOM 树中)立即调用。
//依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。
componentDidMount(){
console.log('---componentDidMount---');
}
//组件将要卸载---调1次,在此方法中执行必要的清理操作。
//例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。
componentWillUnmount(){
console.log('---componentWillUnmount---');
}
//组件更新的“阀门”
shouldComponentUpdate(){
console.log('---shouldComponentUpdate---');
return true
}
//组件将要更新----调用n次,n是更新的次数
UNSAFE_componentWillUpdate(){
console.log('---componentWillUpdate---');
}
UNSAFE_componentWillReceiveProps(){
console.log('---componentWillReceiveProps---');
}
//组件初次渲染+更新---调1+n次
render(){
console.log('---render---');
return(
<div>
<h2>当前状态为:{this.state.count}</h2>
<button onClick={this.add}>更改状态</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>强制更新</button>
</div>
)
}
}
//渲染组件
ReactDOM.render(<LifeHoook/>,document.getElementById('XXX'))
注: 下述生命周期方法即将过时,在新代码中应该避免使用它们;因为这些生命周期方法经常被误解和滥用,此外,预计在异步渲染中,它们潜在的误用问题可能更大
- UNSAFE_componentWillMount()
- UNSAFE_componentWillUpdate()
- UNSAFE_componentWillReceiveProps()