前言
本文介绍react的重中之重,react的组件。组件是react的核心所在,第一部分先介绍组件的基础内容,并且做一个组件能实行盒子左右移动的组件box帮助理解组件内容。
第二部分介绍组件与组件之间的关系以及组件数据与函数的之间的相互调用。
一、component基础
1.安装bootstrap
库:
npm i bootstrap
bootstrap
的引入方式:
import 'bootstrap/dist/css/bootstrap.css';
2.创建Component
创建按钮
当子节点数量大于1时,可以用<div>
或<React.Fragment>
将其括起来。
import React, { Component } from 'react';
class Box extends Component
state = {
x : 0,
colors : [],
};
render() {
return (
<React.Fragment>
<div></div>
<button>left</button>
<button>right</button>
</React.Fragment>
);
}
}
3.内嵌表达式
JSX中使用{}嵌入表达式。
render() {
return (
<React.Fragment>
<div>{this.state.x}</div>
<button>left</button>
<button>right</button>
</React.Fragment>
);
}
4.设置属性
class -> className
CSS属性:background-color -> backgroundColor,其它属性类似
render() {
return (
<React.Fragment>
<div>{this.state.x}</div>
<button className='btn btn-primary m-2'>left</button>
<button className='btn btn-success m-2' >right</button>
</React.Fragment>
);
}
5.数据驱动改变Style
通过修改this.state.x的值,给样式修改不同的style
render() {
return (
<React.Fragment>
<div style={this.getStyle()} >{this.state.x}</div>
<button className='btn btn-primary m-2'>left</button>
<button className='btn btn-success m-2' >right</button>
</React.Fragment>
);
}
getStyle(){
let styles = {
width : "100px",
height: "50px",
color : "white",
lineHeight:"50px",
borderRadius:5,
textAlign: "center",
backgroundColor : "lightblue", //注意都要换成驼峰命名法
marginLeft:this.state.x * 10,
};
if(this.state.x % 2 === 0) {
styles.backgroundColor = 'orange';
}
return styles;
}
6.渲染列表
使用map函数
每个元素需要具有唯一的key属性,用来帮助React快速找到被修改的DOM元素。
state = {
x : 0,
colors : ['red','green','blue'],
};
render() {
return (
<React.Fragment>
<div style={this.getStyle()} >{this.state.x}</div>
<button className='btn btn-primary m-2'>left</button>
<button className='btn btn-success m-2' >right</button>
{this.state.colors.map(color => (
<div key={color}>{color}</div> //key属性用来帮助React快速找到被修改的DOM元素。
))}
</React.Fragment>
);
}
7.Conditional Rendering
利用逻辑表达式的短路原则。
与表达式中 expr1 && expr2
,当expr1
为假时返回expr1
的值,否则返回expr2
的值
或表达式中 expr1 || expr2
,当expr1
为真时返回expr1
的值,否则返回expr2
的值
state = {
x : 0,
colors : [],
};
{this.state.colors.length === 0 && <p>No colors</p>}
8.绑定事件
注意妥善处理好绑定事件函数的this
handleClickLeft = (step) => {
console.log(step);
console.log("click left",this);
}
handleClickRight = () => {
console.log("click right",this);
}
render() {
return (
<React.Fragment>
<div style={this.getStyle()}>{this.state.x}</div>
<button onClick={() => this.handleClickLeft(10)} className='btn btn-primary m-2'>left</button> // 添加了两个事件函数,第一个写法可以传入参数
<button onClick={this.handleClickRight} className='btn btn-success m-2'>right</button> //无法传入参数
</React.Fragment>
);
}
9.修改state
需要使用this.setState()
函数
每次调用this.setState()
函数后,会重新调用this.render()
函数,用来修改虚拟DOM树。React只会修改不同步的实际DOM树节点。
handleClickLeft = (step) => {
this.setState({ //如果想要我们的修改改变render函数,需要调用一下setState
x : this.state.x - 1,
})
}
handleClickRight = () => {
this.setState({
x : this.state.x + 1,
})
}
render() {
return (
<React.Fragment>
<div style={this.getStyle()}>{this.state.x}</div>
<button onClick={() => this.handleClickLeft(10)} className='btn btn-primary m-2'>left</button> // 添加了两个事件函数,第一个写法可以传入参数
<button onClick={this.handleClickRight} className='btn btn-success m-2'>right</button> //无法传入参数
</React.Fragment>
);
}
10.其它:
数组中用filter删除元素方法。
filter会帮我们遍历数组内的每一个值,如果返回值会false则保留,true则删除元素
state = {
solutions : [
{key : 0,number : 1164,title :"背包问题",views:2930},
{key : 1,number : 1165,title :"背包问题",views:2930},
{key : 2,number : 1166,title :"背包问题",views:2930},
{key : 3,number : 1166,title :"背包问题",views:2930},
{key : 4,number : 1167,title :"背包问题",views:2930},
{key : 5,number : 1168,title :"背包问题",views:2930},
{key : 6,number : 1169,title :"背包问题",views:2930},
{key : 7,number : 1113,title :"背包问题",views:2930},
],
}
handleClickdelete = (s) => {
const solutions = this.state.solutions.filter(solution => solution !== s); //filter会帮我们遍历数组内的每一个值,如果返回值会false则保留,true则删除元素
this.setState({
solutions : solutions,
}) ;
}
render{
...
<button onClick={() => this.handleClickdelete(solution)} className='btn btn-danger'>删除</button>
}
二、Component的组合运用
首先创建APP,Boxes,Navber组件,APP组件中包含着 Boxes组件和Navber组件,并且让Boxes组件中包含一系列Box组件。
1. 从上往下传递数据
通过this.props
属性可以从上到下传递数据。
此时Boxes为App的子结点,父节点传给子节点即可用props
属性。
App在Boxes标签中插入要传入的元素即可,可以传递变量,数据,函数,对象,类无论啥都可以传递。
App类组件中的render函数
render() {
return (
<React.Fragment>
<NavBar boxesCount={this.state.boxes.length}/>
<Boxes
boxes={this.state.boxes}
onReset={this.handleReset}
onLeft={this.handleClickLeft}
onRight={this.handleClickRight}
onDelete={this.handleDelete}
/>
</React.Fragment>
);
}
Boxes里调用父组件的就是用props
属性。
例子:
class Boxes extends Component {
render() {
return (
<React.Fragment>
<button onClick={this.props.onReset} style={{marginBottom: "15px"}} className='btn btn-info'>Reset</button>
{this.props.boxes.map(box => (
<Box key={box.id} //Boxes组件传给Box组件
id = {box.id}
x = {box.x}
box = {box}
name = "lyy"
onDelete={this.props.onDelete}
onLeft={() => this.props.onLeft(box)}
onRight={()=> this.props.onRight(box)}
></Box>
))}
</React.Fragment>
);
}
}
2.传递子节点
通过this.props.children
属性传递子节点
在上面Boxes组件中,我们在Box标签中间插入了其它标签,就是我们通过children传入给Box的子结点。
Boxes中的父标签插入子节点:
<Box key={box.id}>
<h1>Box</h1>
<p>#{box.id}</p>
</Box>
Box标签调用子节点:
class Box extends Component {
state = {
};
render() {
return (
<React.Fragment>
{this.props.children}
</React.Fragment>
);
}
}
3.从下往上调用函数
注意:每个组件的this.state
只能在组件内部修改,不能在其他组件内修改。
例:Boxes组件调用父组件App的函数:
<React.Fragment>
{this.props.boxes.map(box => (
<Box
onDelete={this.props.onDelete}
onLeft={() => this.props.onLeft(box)}
onRight={()=> this.props.onRight(box)}
>
</Box>
))}
</React.Fragment>
4.state数据的存储
每个维护的数据仅能保存在一个this.state
中
不要直接修改this.state的值,因为setState函数可能会将修改覆盖掉。
例如我们App组件中的handleClickLeft函数:
handleClickLeft = (box) => {
const boxes = [...this.state.boxes]; //这里浅拷贝一个数组
const k = boxes.indexOf(box); //索引box的下标
boxes[k].x --; //修改box的x
this.setState({boxes : boxes}); // 传给setState 为什么这里要大费周折复制一个数组在赋值呢? 如果直接修改
// state的值,可能会让DOM的数值不同步,因为react的DOM树需要setState才能更换,所以这样做可以使数据保持同步
}
5.兄弟结点之间组件的调用
要将多个组件共用的数据存放到最近公共祖先的this.state
中。
类似 Boxes和Navber 这两个组件,他们的公用数据都放在App组件中。
class App extends Component {
state = {
boxes : [
{id:1,x : 1},
{id:2,x : 2},
{id:3,x : 3},
{id:4,x : 4},
]
}
handleClickLeft = (box) => {
console.log("handle click left");
const boxes = [...this.state.boxes]; //这里浅拷贝一个数组
const k = boxes.indexOf(box); //索引box的下标
boxes[k].x --; //修改box的x
this.setState({boxes : boxes}); // 传给setState 为什么这里要大费周折复制一个数组在赋值呢? 如果直接修改
// state的值,可能会让DOM的数值不同步,因为react的DOM树需要setState才能更换,所以这样做可以使数据保持同步
}
handleClickRight = (box) => {
console.log("handle click right");
const boxes = [...this.state.boxes];
const k = boxes.indexOf(box);
boxes[k].x ++;
this.setState({boxes : boxes});
}
handleReset = () => {
const boxes = this.state.boxes.map(b=> {
return {
id: b.id,
x : 0
}
});
this.setState({boxes});
}
handleDelete = (boxId) => {
const boxes = this.state.boxes.filter(
b => b.id !== boxId
);
this.setState({boxes:boxes});
}
render() {
return (
<React.Fragment>
<NavBar boxesCount={this.state.boxes.length}/>
<Boxes
boxes={this.state.boxes}
onReset={this.handleReset}
onLeft={this.handleClickLeft}
onRight={this.handleClickRight}
onDelete={this.handleDelete}
/>
</React.Fragment>
);
}
}
6.无状态函数组件
当组件中没有用到this.state
时,可以简写为无状态的函数组件。
以Navber组件为例:
类组件写法
class NavBar extends Component {
state = { }
render() {
return (
<nav className="navbar navbar-light bg-light">
<div className="container-fluid">
<a className="navbar-brand">Navbar
<span>Boxes Count: {this.props.boxesCount}</span>
</a>
</div>
</nav>
);
}
}
函数组件写法 (函数的传入参数为props对象)
const NavBar= (props) => {
return (
<nav className="navbar navbar-light bg-light">
<div className="container-fluid">
<a className="navbar-brand">Navbar
<span>Boxes Count: { props.boxesCount}</span>
</a>
</div>
</nav>
);
}
7.组件的生命周期
Mount
周期,执行顺序:constructor() -> render() -> componentDidMount()
Update
周期,执行顺序:render() -> componentDidUpdate()
Unmount
周期,执行顺序:componentWillUnmount()
总结
Component是我们学习react的关键,掌握组件的方法就是迅速造几个简易的轮子来理解。