一、创建组件
方法一:构造函数创建组件
注意:
- 使用构造函数来创建组件,必须要向外return一个合法的JSX创建的虚拟DOM;
- 使用构造函数来创建组件,用props接收外部传进来的数据。
// 使用组件并 为组件传递 props 数据
<Hello name={dog.name} age={dog.age} gender={dog.gender}></Hello>
// 在构造函数中,使用 props 形参,接收外界 传递过来的数据
function Hello(props) {
// props.name = 'zs'
console.log(props)
// 结论:不论是 Vue 还是 React,组件中的 props 永远都是只读的;不能被重新赋值;
return <div>这是 Hello 组件 --- {props.name} --- {props.age} --- {props.gender}</div>
}
方法二:class关键字创建组件
ES6 中 class 关键字,是实现面向对象编程的新形式;
必须 让自己的组件,继承自 React.Component;
在 组件内部,必须有 render 函数,作用:渲染当前组件对应的 虚拟DOM结构
import React from ‘react’
class 组件名称 extends React.Component {
constructor(){
super();
this.state=['a','b','c'];
};
render(){
// render 函数中,必须 返回合法的 JSX 虚拟DOM结构
return <div>这是 class 创建的组件</div>
}
}
两种创建方式的对比
构造函数-无状态组件 | class关键字-有状态组件 | |
---|---|---|
使用场景 | 组件内,不需要有私有的数据时使用。 | 组件内,需要有自己私有的数据。 |
props 中的数据:都是外界传递到组件中的;都是只读的; | 存在 | 存在 |
state 中的数据:都是组件内私有的;都是可读可写的; | 不存在 | 存在 |
生命周期函数 | 不存在 | 存在 |
二、props属性&state状态
1.props属性
- this.props:获取外部传入的数据
//新建一个组件AddUser.jsx
import React from 'react';
export default Class AddUser extends React.Component {
render(){
return (
<div class="wrap-adduser">
{/* this.props:获取外部传入的数据。*/}
<div>名字:{this.props.name}</div>
<div>年龄:{this.props.age}</div>
</div>
)
}
}
- 使用ReactDOM:把组件 render(挂载) 到对应的DOM节点上
let adduer =<div class="wrap-adduser">
{/* this.props:获取外部传入的数据。*/}
<div>名字:{this.props.name}</div>
<div>年龄:{this.props.age}</div>
</div>
//app.jsx
import {render} from 'react-dom';
import AddUser from './adduser'
const props = {
name:'张三',
age:18
};
//render (<AddUser name='张三' age=18>,document.getElementById('container'));
//下面的写法是JSX利用ES6语法生成的遍历的功能------属性扩散
render (<AddUser {...props}>,document.getElementById('container'));
- 验证props传入的属性:使用react中的PropTypes验证数据类型
import {PropTypes} from 'react';
const propTypes = {
//验证不同类型的JS变量
optionalArray:PropTypes.array,
optionalBool:PropTypes.bool,
optionalFunc:PropTypes.func,
optionalNumber:PropTypes.number,
optionalObject:PropTypes.object,
optionalString:PropTypes.string,
//可以是一个ReactElement类型
optionalElement:PropTypes.element,
//可以是别的组件的实例
optionalMessage:PropTypes.instanceOf(Message),
//可以规定一组值其中的一个
optionalEnum:PropTypes.oneOfType(['News','Photos']),
//可以规定一组类型中的一个
optionalUnion:PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.oneOfType(Message)
]),
//可以在最后加一个isRequired,表明这个属性是必需的,否则会返回一个错误
requiredFunc:React.PropTypes.func.isRequired
}
在接收props数据的组件中添加验证 :
import React ,{PropTypes} from 'react';
//需要验证的属性
const propTypes ={
name:PropTypes.string.isRequired,
gender:PropTypes.number.isRequired
};
class AddUser extends React.Component {
render(){
return (
<div class="wrap-adduser">
{/* this.props:获取外部传入的数据。*/}
<div>名字:{this.props.name}</div>
<div>年龄:{this.props.age}</div>
</div>
)
}
}
//将验证赋值给AddUser这个组件的propTypes属性
AddUser.propTypes=propTypes ;
export default AddUser;
2.state状态
- 在 constructor中用this.state 定义它的值,根据值来渲染不同的UI;
- 当state的值发生改变时,可通过 this.setState方法让组件再次调用render方法,再次渲染UI。
例:点赞功能:点击一次,赞的次数加1
//Good.jsx
export default class Good extends React.Component {
constructor (props) {
super(props);
//定义初始的值
this.state={
linked:0
};
//改变this的指向
this.linkedCB=this.linkedCB.bind(this);
}
}
linkedCB(){
let linked=this.state.linked;
改变初始的值,并渲染页面
linked++;
this.setState({
linked
})
}
render(){
return(
<div>
<button onClick={this.linkedCB}> 点个赞吧 </button>
<span>点赞总数:{this.state.linked}</span>
</div>
)
}
注意:React组件通过props和state的值,使用render方法生成一个组件的实例。
- 什么组件应该有state,而且应该遵循最小化state的准则?即让大多数的组件处于无状态的。
- 创建尽量多的无状态组件,这些组件只关心渲染数据;
- 这些组件的外层是一个包含state的父级组件,这个组件用于处理各种事件,交流逻辑,修改state;对应的子组件只要关心传入的属性就好。
- state应该包含什么数据?不应该包含什么数据?
应该包含的数据:包含组件的事件回调函数可能引发UI更新的这类数据。且根据最小化设计state的原则,当数据有交叉的部分时,小范围的数据只需保存ID或索引就好。
不应该包含的数据:
- 可以由state计算得出的数据。(小范围的数据)
- 组件。(不需要保存到state,只用在render函数中渲染就好)
*props中的数据。(props可以看做是组件的数据源,它不需要保存到state中)
3.state中的值(单向数据流)
- 在 React 中,默认只是单向数据流,也就是 只能把 state 上的数据绑定到 页面,无法把 页面中数据的变化,自动同步回 state 。
- 如果需要把 页面上数据的变化,保存到 state,则需要程序员手动监听文本框的onChange事件,拿到最新的数据,手动调用this.setState({ }) 更改回去;
- 案例
<input type="text" style={{ width: '100%' }} value={this.state.msg} onChange={() => this.textChanged()} ref="mytxt" />
// 响应 文本框 内容改变的处理函数
textChanged = () => {
// console.log(this);
// console.log(this.refs.mytxt.value);
this.setState({
msg: this.refs.mytxt.value
})
}
三、组件的生命周期
组件初始化
- getDefaultProps:只在装载前调用一次;
- getInitialState:只在装载前调用一次,这个函数的返回值会被设置到this.state中;
- componentWillMount:在render之前调用,可做渲染前的一些准备;
- render
- componentDidMount:只在装载完成后调用一次,render之后执行,在这里可以获得到组件的DOM结构。相当于vue中的mounted。
组件props更新
当组件接收到新的props时,会依次触发下面的方法
- componentWillReceiveProps(object,nextProps):接收到新的props时触发,nextProps即为新的的props,this.props为旧的props。
- shouldComponentUpdate:重新调用render之前执行,返回一个布尔值,返回false则组件不会被更新,之前的流程都不会被触发,默认返回true,组件更新。
- componentWillUpdate:在render之前调用,可做渲染前的一些准备,与componentWillMount类似。
- render
- componentDidUpdate:重新渲染完成后立刻调用,类似于componentDidMount。
组件卸载
-componentWillUnmount:组件卸载和销毁之前调用的方法。
四、组件的组合使用
例.渲染一个爱好列表
//Hobby.jsx 子组件
import React,{PropTypes} from 'react';
const propTypes={
hobby:Proptype.string.isRequired
};
class Hobby extends ReactComponent(){
render(){
return <li>{this.props.hobby}</li>
}
}
Hobby.propTypes=propTypes;
export default Hobby;
//HobbyLIst.jsx 父组件
import Hobby from './hobby';
...
constructor(props){
super(props);
//在state中添加两个爱好
this.state = {
linked:0,
hobbies:['music','dance']
};
}
...
render(){
return (
<div>
<h2>爱好列表</h2>
<ul>
{this.state.map((hobby,k)=>
<Hobby key={k} hobby={hobby} />
)}
</ul>
</div>
)
}
注意:要给每个循环组件添加一个唯一的key值。
五、DOM操作
在大多数情况下,不需要进行DOM操作去更新UI,应该使用setState来重新渲染UI。但是有一些特殊情况,确实 需要访问一些DOM结构:可以采用refs的方式获得DOM节点。
//例.添加爱好 HobbyList.jsx
render() {
return(
<div>
...
<input type="text" ref="addhobby">
<button onclick={this.addHobbyCB}>点击添加爱好</button>
</div>
)
}
...
addHobbyCB(){
// 用this.refs.name来获取DOM节点
let hobbyInput=this.refs.hobby;
let val = hobbyInput.value;
if (val) {
let hobbies = this.state.hobbies;
// 添加值到数组中
hobbies=[...hobbies,val];
// 更新state,熏染UI
this.setState({hobbies},()=>{hobbyInput.value='';})
}
}