React组件
组件允许你将 UI 拆分为独立可复用的代码片段
React组件的分类
React 的组件可以定义为 class 或函数的形式
class式组件中有着更多的方法与属性
函数式组件
定义一个函数,使其返回一个虚拟DOM就是函数式组件了
function NewComponent(){
return <h1>Hello World</h1>;
}
类式组件
定义class组件,想要继承一个内置的class:React.Component
class Welcome extends React.Component {
render() {
return <h1>Hello, World</h1>;
}
}
render() 函数必须被定义,它的返回值与函数式组件一样,都是你想要显示到浏览器上的DOM
组件渲染到页面
我们可以通过书写HTML标签一样的方式将组件渲染到页面中
ReactDOM.render(<Weather/>,document.getElementById('test'))
由于JSX的语法要求,组件的首字母必须大写
组件实例的三大属性
由于函数式组件没有组件实例,所以三大属性一般是class组件使用,但最新的hooks写法可以让函数式组件使用三大属性
state属性
state属性是组件实例上的一个对象,组件的state驱动着页面发生改变
换句话说,我们可以通过改变state属性使页面发生改变,也就是我们需要的交互
初始化state属性
我们可以在构造器constructor上初始化state属性
constructor(props){
super(props)
this.state = {isHot:true} //初始化状态
}
注意:state属性必须是一个对象
下面我们通过一个实例演示,怎么通过state属性改变页面
class Weather extends React.Component{
constructor(props){
super(props)
this.state = {isHot:true} //初始化状态
this.changeWeather = this.changeWeather.bind(this)
}
render(){
return <h1 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h1>
}
changeWeather() {
const isHot = this.state.isHot
this.setState({isHot:!isHot}) //取反赋回去
}
}
//渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
在h1标签中绑定一个onClick时间,点击后调用changeWeather方法,改变isHot状态,页面重新渲染显示改变后的天气
更改state时,必须使用React的一个内置函数setState()进行修改,不可以直接对state进行修改
如上面的changeWeather()方法
changeWeather() {
const isHot = this.state.isHot
this.state.isHot=!isHot //取反赋回去
//这样写是错误的
}
直接修改state中的isHot,将导致页面不能更新
类中方法this指向问题
类中方法的this指向实例自身,当onClick事件被触发后,是onClick帮我们调用方法,不是在实例中被调用,因此就会出现this的值是undefined
怎么解决这个问题,我们可以使用bind(this)给为事件处理函数绑定实例
constructor(props){
super(props)
this.changeWeather = this.changeWeather.bind(this)
}
这样处理后,事件触发后this能正确指向
不写constructor()
上面的代码其实不写constructor也是可以的
class Weather extends React.Component{
state = {isHot:true} //初始化状态
render(){
return <h1 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h1>
}
changeWeather= ()=> {
const isHot = this.state.isHot
this.setState({isHot:!isHot}) //取反赋回去
}
}
//渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
在class中,直接添加一个state属性就可以完成对状态的初始化,this的指向问题也可以用箭头函数解决
props属性
props属性存了外部传入的数据,与state属性类似也是一个对象
怎么在外部传入数据
//渲染组件到页面
ReactDOM.render(<Weather wind = 9/>,document.getElementById('test'))
渲染组件到页面时,给组件标签添加的属性将作为一个对象传入到props属性中
注意:
props属性是无法在组件内部进行修改的,只能进行调用
给传入的props做限制与设立初始值
给props做限制,可以使用propTypes
MyConponent.propTypes = {
name:PropTypes.string.isRequired,
age:PropTypes.number,
sex:PropTypes.string
}
给MyConponent类本身添加一个propTypes属性,给需要限制的props添加PropTypes.types属性,就可以限制类型
PropTypes.isRequired就是强制要求该类型
自 React v15.5 起,React.PropTypes 已移入另一个包中。需要 prop-types 库 代替
给props设立初始值,可以使用defaultProps
MyConponent.defaultProps = {
age:18
}
属性对应的对象就是props的初始值
refs属性与事件处理
获取节点,原生JS提供了很多方法如,getElementById
React提供一个refs属性帮助我们获取到节点
- 字符串形式
render(){
return (
<div>
<input type="text" ref="input1"/>
<button onClick={this.show}>点我提示左侧数据</button>
<input type="text" ref="input2" onBlur={this.show2} placeholder="失去焦点提示数据"/>
</div>
)
给返回的元素添加一个ref属性,属性的值为一个字符串,则this.refs.字符串中会存有ref属性对应的DOM节点
2. 回调函数形式
render(){
return (
<div>
<input type="text" ref={c => this.input1 = c}/>
<button onClick={this.show}>点我提示左侧数据</button>
<input type="text" ref={c => this.input2 = c} onBlur={this.show2} placeholder="失去焦点提示数据"/>
</div>
)
}
给ref属性中写入一个回调函数,此回调函数中的参数就是对应的DOM节点
3. creactRef形式
class Demo extends React.Component{
container = React.createRef()
container2 = React.createRef()
render(){
return (
<div>
<input type="text" ref={this.container} />
<button onClick={this.show}>点我提示左侧数据</button>
<input type="text" ref={this.container2} onBlur={this.show2} placeholder="失去焦点提示数据"/>
</div>
)
}
show = ()=>{
alert(this.container.current.value)
}
show2 = ()=>{
alert(this.container2.current.value)
}
}
组件的生命周期
组件的挂载(mount)与卸载(unmount)便是组件的整个生命周期
在下面这个案例,我们希望设立一个计时器让h1标签的透明度不断发生变化
class Life extends React.Component{
state = {opacity:1}
render(){
setInterval(() => {
//1.获取原来的opacity
let {opacity} = this.state
//2.递减
opacity -= 0.1
if(opacity <= 0) opacity = 1
//3.赋回去
this.setState({opacity})
}, 200);
const {opacity} = this.state
return (
<div>
<h1 style={{opacity}}>组件</h1>
<button onClick={this.death}>卸载组件</button>
</div>
)
}
death = ()=>{
//卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
}
ReactDOM.render(<Life/>,document.getElementById('test'))
将计数器写在render()方法中,每进行一次setState render就调用一次导致计数器无限累加
我们只需要调用一次计数器,这时我们可以使用一些React内置的方法
componentDidMount()
在组件挂载完毕后会自动调用一次componentDidMount()方法
componentDidMount(){
this.timer = setInterval(()=>{
let {opacity}=this.state;
opacity-=0.001;
if(opacity<=0){
opacity=1
}
this.setState({opacity:opacity})
},1)
}
将计数器写在componentDidMount()方法中,组件挂载完毕后自动调用
componentWillUnmount()
如果我们直接将组件卸载的话,会出现一个严重的错误。组件已经被卸载了,计数器还在继续计数。因此,在组件将于被卸载时,将计数器停止
这时就用到了componentWillUnmount()方法
componentWillUnmount(){
clearInterval(this.timer)
}
以上两个方法就是生命周期钩子函数,它可以在组件运行到特定阶段时被调用
下面是组件完整的生命周期流程图: