通过函数定义组件:js函数可以接收一个props属性,并返回一个React组件
function MsgBox(props){
return hello {props.name}
}
通过类继承定义组件:通过继承React.Component产生一个类组件,通过this.props访问类属性,并在render函数中返回react组件。
class MsgBox extends React.Component{
render() {
return Hello {this.props.name}
}
}
注意:组件名称必须以大写字母开头。例如
表示一个DOM标签,但通过ReactDOM.render()函数将组件MsgBox渲染到页面
ReactDOM.render(
, //要渲染的组件
document.getElementById('app'), //组件渲染的目标位置
()=>{ //渲染结束后的回调函数
console.log("render回调");
}
)
在渲染前,React DOM 首先会比较元素内容先后的不同,而在渲染过程中只会更新改变了的部分。
条件渲染:可以为组件根据不同条件定义不同的返回值,实现条件渲染,或者可以将不同的返回值先储存在元素变量中,最后再统一与其他组件部分一起返回。例如根据用户的登录状态定义不同的提示语保存在变量prompt中,最后将变量prompt与标签button放在一个div中一起return,当用户点击button切换登录状态时,显示不同的提示语。
class Login extends React.Component{
constructor(props){
super(props);
this.state={
hasLogin:true
};
this.shiftLog=this.shiftLog.bind(this);
}
shiftLog(){
this.setState({
hasLogin:!this.state.hasLogin
})
}
render(){
let prompt;
if(this.state.hasLogin){ //根据hasLogin的不同值,设置变量prompt的值
prompt= 欢迎你!
}else{
prompt= 请登陆!
}
return(
{prompt} //渲染变量prompt
)
}
}
阻止渲染:如果不希望一个组件进行渲染的话,可以让组件返回null,例如当hide为true时,不渲染组件Warning:
function Warning(props) {
if(props.hide){
return null; //不渲染组件Warning
}
return show warning
}
ReactDOM.render(
,
document.getElementById('app')
);
我们可以定义子组件,然后在父组件中使用子组件,注意最后返回只能有一个根div标签。
//子组件
function Child(props) {
return 这是{props.name}
;
}
//父组件
function Parent() {
return (
//返回一个根标签
)
}
ReactDOM.render(
,
document.getElementById('app')
}
)
渲染结果:
提取组件:相反的思路,当我们将一个传统的页面组件化为一个react页面时,我们可以将其中重复使用的部分抽象为一个组件,使得页面更为简洁、易于复用。在子组件的基础上,还可以将其外围容器抽象为父组件,层层嵌套,简化开发。
组件容器:有时候我们的组件作为一个容器,并不知道要使用什么子组件,这时我们可以使用props.children来暂时代替子组件,当子组件插入时再替换到那个位置
function Parent(props) {
return (
父组件
{props.children} //将传入不同的children属性渲染在此位置
)
}
function Child() {
return 子组件
}
function Composition() {
return(
}/> //通过属性的方式传递 模块渲染到父容器中
)
}
如果父组件容器有多个插槽,我们想把子组件放到指定的插槽中,这时就应该自定义属性名来指定插槽的名字。例如父组件容器有两个插槽,一个left,一个right,我们希望把
function Left() {
return 左边
}
function Right() {
return 右边
}
function Container(props) {
return (
父组件容器
{props.left}
{props.right}
)
}
ReactDOM.render(
} right={ } />, //通过不同属性将不同组件渲染到指定位置
document.getElementById('app')
)
渲染结果如下:
在父组件容器Container中通过自定义属性props.left与props.right设置了两个插槽,在使用
React规定任何组件对于它的props属性只可以访问,而不可以修改
由于组件无法对props属性进行更改,如果要实现一个动态变化的时钟,只能通过setInterval每隔一秒执行一次tick渲染函数,重新渲染组件,上层动态地传入数据,然后利用props读取显示。
function Clock(props) {
return 现在的时间是:{props.date.toLocaleTimeString()}
}
function tick() {
ReactDOM.render(
,
document.getElementById('app')
);
}
setInterval(tick,1000);
以上重新渲染静态props很不方便,为了实现组件内部数据动态更新,需要使用React的状态(state)来储存变量,state允许组件对其进行更改。state只能在以类方式定义的组件中使用。
5.1读取state:通过this.state.date可以访问到状态date
5.2初始化state:通过类的构造函数constructor完成state的初始化
class Clock extends React.Component{
constructor(props){
super(props); //调用父类的构造方法对props属性进行初始化
this.state={
date:new Date() //对date初始化为一个Date对象
}
}
}
5.3修改state:state不可以直接对其进行修改,而是通过setState()函数对其进行修改,例如定义tick函数对date的更新
tick(){
this.setState({
date:new Date()
})
}
5.4state异步更新:state的更新是异步的,我们不能依赖它来进行实时计算。例如设置一个flag,通过按钮点击来切换flag的值,当第一次点击时setState会将flag(初始值false)取反设为true,但是由于state异步更新,它不会立即将this.flag更新为true,当再次点击时,setState会认为this.flag仍然是false,便又将flag设为true,无法将flag设为false。
this.setState({
flag:!this.flag
});
要弥补这个问题,使用另一种 setState() 的形式,它接受一个函数而不是一个对象。这个函数将接收前一个状态prevState作为第一个参数,这样就可以实现flag在上一个状态flag的基础上取反
this.setState((prevState) =>({
flag: !prevState.flag
}))
当把一个数组内的元素渲染到页面时,需要使用列表
function Number(props) {
let listNum=props.number.map(
(val,index)=> //ES6函数的简写:(值,索引)
{val} // => 遍历每个元素的返回值
);
return
{listNum}
;
}
const numArr=['one','two','three'];
ReactDOM.render(
,
document.getElementById('app')
);
渲染结果为:
注意:key值要求在兄弟元素之间唯一,但不同数组的元素可以相同。
key只用于react区分提示,元素无法通过props.key值来访问key的值。
JSX的大括号中可以嵌套任何表达式,因此可以直接将遍历返回的
return (
{ //可直接在{}内放js表达式并返回显示的标签内容
props.number.map((val,index)=>
- {val}
)
}
);
组件从创建到被销毁有一个生命周期:挂载、更新、销毁,在每个周期的前后都有相应的钩子函数,当组件的生命进行到对应阶段时,钩子函数就会被执行,可以在钩子函数中调用函数来完成组件在对应生命周期的操作。例如在组件挂载完成后的钩子componentDidMount(){}设置一个定时器,定时调用tick()来更新date,当state.date发生变化时,react会重新渲染组件,从而实现时钟效果。当组件被销毁前在钩子componentWillUnmount(){}内将定时器清除。
componentDidMount(){ //组件挂载后执行
this.timer=setInterval(()=>this.tick(),1000);
}
componentWillUnmount(){ //组件销毁前执行
clearInterval(this.timer);
}
state通常被称为局部或封装。 除了拥有并设置它的组件外,其它组件不可访问。但是它可以作为参数传递给子组件,在子组件中通过props属性可以使用从上层传递下来的值,但并且不知道它是来自 state、props还是手工输入。例如以下三种方式传递参数:
function FormattedDate(props) { //在组件中使用props.date,但并不知道上层数据的具体形式
return It is {props.date.toLocaleTimeString()}.
;
}
这通常被称为自顶向下的数据流。 state始终由某些特定组件所有,并且该组件的数据只能对子组件造成影响。