1.创建Boxes组件
Boxes组件中包含一系列Box组件。
之前已经写好了一个box.js文件,里面有一个Box类,这个类就可以引入到Boxes.js文件作为一个标签使用。
box.js
import React, { Component } from 'react';
class Box extends Component {
state = {
x : 0,
}
handleClickLeft = (step) => {
this.setState({
x : this.state.x - step,
});
}
handleClickRight = (step) => {
this.setState({
x:this.state.x + step
});
}
render() {
return (
<React.Fragment>
<div style = {this.getStyle()}>{this.toString()}</div>
<button onClick={ () => this.handleClickLeft(10)} className="btn btn-warning m-2">left</button>
<button onClick={() => this.handleClickRight(10)} className='btn btn-secondary m-2'>right</button>
</React.Fragment>
);
}
getStyle(){
let style={
width:"50px",
height:"50px",
backgroundColor:"lightblue ",
color:"white",
textAlign:"center",
lineHeight:"50px",
borderRadius:"5px",
marginLeft: this.state.x //随着x的变化,方块的位置也会移动
}
if(this.state.x === 0) style.backgroundColor="orange";
return style;
};
toString(){
return "x:"+ this.state.x;
}
}
export default Box;
boxes.js
import React, { Component } from 'react';
import Box from './box'
class Boxes extends Component {
state = {
boxes: [
{id: 1, x: 0},
{id: 2, x: 0},
{id: 3, x: 0},
{id: 4, x: 0},
]
}
render() {
return (
<React.Fragment>
{this.state.boxes.map(box => (
<Box key={box.id}/>
))};
</React.Fragment>
);
}
}
export default Boxes;
2.从上往下传递数据
通过this.props
属性可以从上到下传递数据。
通过标签传数据,在标签内定义之后,在定义标签的类里通过this.props可以得到定义的数据,定义时对应的值要加大括号
例如:
boxes.js
render() {
return (
<React.Fragment>
{this.state.boxes.map(box => (
<Box
key={box.id}
x = {2}
box
/>
))};
</React.Fragment>
);
}
box.js
render() {
console.log(this.props) ;
return (
<React.Fragment>
<div style = {this.getStyle()}>{this.toString()}</div>
<button onClick={ () => this.handleClickLeft(10)} className="btn btn-warning m-2">left</button>
<button onClick={() => this.handleClickRight(10)} className='btn btn-secondary m-2'>right</button>
</React.Fragment>
);
}
输出:
3.传递子节点
通过this.props.children
属性传递子节点
如果是标签嵌套,那么标签内部的孩子也会传递过去
例如:
box.js
render() {
console.log(this.props) ;
return (
<React.Fragment>
{this.props.children} //调用子节点
<div style = {this.getStyle()}>{this.toString()}</div>
<button onClick={ () => this.handleClickLeft(10)} className="btn btn-warning m-2">left</button>
<button onClick={() => this.handleClickRight(10)} className='btn btn-secondary m-2'>right</button>
</React.Fragment>
);
}
boxes.js
render() {
return (
<React.Fragment>
{this.state.boxes.map(box => (
<Box
key={box.id}
x = {box.x}
box
>
<h1>Box:</h1> //子节点
<p>#{box.id}</p> //子节点
</Box>
))};
</React.Fragment>
);
}
输出:
并且,如果有多个子节点,可以通过下标来获取某个子节点,第一个下标为0(this.props.children[0]),以此类推
Box在Boxes里面作为标签,也是Boxes的子节点,如果想在Box里面调用上层结点Box的函数,利用3.传递子节点,将父节点的函数作为参数传递给子节点,然后子节点通过this.props.函数名调用。
例如:
box.jsx
render() {
//console.log(this.props) ;
return (
<React.Fragment>
<div style = {this.getStyle()}>{this.toString()}</div>
<button onClick={ () => this.handleClickLeft(10)} className="btn btn-warning m-2">left</button>
<button onClick={() => this.handleClickRight(10)} className='btn btn-secondary m-2'>right</button>
<button onClick={() => this.props.delete(this.props.id)} className='btn btn-danger m-2'>delete</button> //通过参数调用函数 因为要传参,所以使用匿名函数
</React.Fragment>
);
}
boxes.jsx
handleDelete = (boxId) => {
const boxes = this.state.boxes.filter(
b => b.id !== boxId
);
this.setState({boxes});
}
render() {
return (
<React.Fragment>
{this.state.boxes.map(box => (
<Box
key={box.id}
x = {box.x}
delete = {this.handleDelete}
id = {box.id}
/>
))};
</React.Fragment>
);
}
4.从下往上调用函数
注意:每个组件的this.state只能在组件内部修改,不能在其他组件内修改。
比如box和boxes里面都有一个state.x,如果想在boxes修改box里面的state.x,是不可行的,想要修改的方法就是值保留一个state,保留boxes的state,然后将state作为参数从boxes传递给box使用即可
5.每个维护的数据仅能保存在一个this.state中
不要直接修改this.state
的值,因为setState函数
可能会将修改覆盖掉。
6.创建App组件
包含:
导航栏组件
Boxes组件
注意:
要将多个组件共用的数据存放到最近公共祖先的this.state中。
App组件里面有导航栏组件和Boxes组件,如果想在导航栏组件和Boxes组件之间互传信息,就必须将公共信息放在App组件里,这样可以通过向子节点传参的方式传给两个子节点组件
比如要NavBar组件传state.boxes的元素个数,给Boxes组件传state.boxes和所有的方法
因为两个组件共用state,所以state放在App里
render() {
return (
<React.Fragment>
<NavBar boxes={this.state.boxes.length}/>
<div className="container">
<Boxes
boxes = {this.state.boxes}
onReset={this.handleReset}
onClickLeft={this.handleClickLeft}
onClickRight={this.handleClickRight}
onDelete={this.handleDelete}
/>
</div>
</React.Fragment>
);
}
7.无状态函数组件
当组件中没有用到this.state
时,可以简写为无状态的函数组件。
函数的传入参数为props
对象
修改前:
boxes.js
import React, { Component } from 'react';
import Box from './box'
class Boxes extends Component {
render() {
return (
<React.Fragment>
<button
onClick={this.props.onReset}
style={{marginBottom: "15px"}} className='btn btn-danger'>Reset</button>
{this.props.boxes.map(box => (
<Box
key={box.id}
box = {box}
clickLeft = {() => this.props.onClickLeft(box)}
clickRight = {() => this.props.onClickRight(box)}
delete = {this.props.onDelete}
/>
))};
</React.Fragment>
);
}
}
export default Boxes;
修改为无函数组件后:
其实就是将原来render()函数里面的return…复制到Boxes里面,将所有的this去掉
boxes.js
import React, { Component } from 'react';
import Box from './box'
const Boxes = (props) => {
return (
<React.Fragment>
<button
onClick={props.onReset}
style={{marginBottom: "15px"}} className='btn btn-danger'>Reset</button>
{props.boxes.map(box => (
<Box
key={box.id}
box = {box}
clickLeft = {() => props.onClickLeft(box)}
clickRight = {() => props.onClickRight(box)}
delete = {props.onDelete}
/>
))};
</React.Fragment>
);
}
export default Boxes;
8.组件的生命周期
Mount
周期,执行顺序:constructor() -> render() -> componentDidMount()
Update
周期,执行顺序:render() -> componentDidUpdate()
Unmount
周期,执行顺序:componentWillUnmount()