一、条件和列表渲染
条件渲染
class App extends React.Component {
state = {
isLogin: true,
};
render() {
const { isLogin } = this.state;
// 1.方案一:通过if判断: 逻辑代码非常多的情况
let welcome = null;
let btnText = null;
if (isLogin) {
welcome = <h2>欢迎回来~</h2>;
btnText = "退出";
} else {
welcome = <h2>请先登录~</h2>;
btnText = "登录";
}
return (
<div>
{welcome}
{/* 2.方案二: 三元运算符 */}
<button onClick={(e) => this.loginClick()}>
{isLogin ? "退出" : "登录"}
</button>
<hr />
<h2>{isLogin ? "你好啊, yunmu" : null}</h2>
{/* 3.方案三: 逻辑与&& */}
{/* 逻辑与: 一个条件不成立, 后面的条件都不会进行判断了 */}
<h2>{isLogin && "你好啊, yunmu"}</h2>
{isLogin && <h2>你好啊, yunmu</h2>}
</div>
);
}
loginClick() {
this.setState({
isLogin: !this.state.isLogin,
});
}
}
条件渲染-v-show效果
class App extends React.Component {
state = {
isLogin: true,
};
render() {
const { isLogin } = this.state;
const titleDisplayValue = isLogin ? "block" : "none";
return (
<div>
<button onClick={(e) => this.loginClick()}>
{isLogin ? "退出" : "登录"}
</button>
<h2 style={{ display: titleDisplayValue }}>你好啊, yunmu</h2>
</div>
);
}
loginClick() {
this.setState({
isLogin: !this.state.isLogin,
});
}
}
列表渲染
class App extends React.Component {
this.state = {
names: ["abc", "cba", "nba", "mba", "dna"],
numbers: [110, 123, 50, 32, 55, 10, 8, 333],
};
render() {
return (
<div>
<h2>名字列表</h2>
<ul>
{this.state.names.map((item) => {
return <li>{item}</li>;
})}
</ul>
<h2>数字列表(过滤1)</h2>
<ul>
{this.state.numbers.filter((item) => {
return item >= 50;
}).map((item) => {
return <li>{item}</li>;
})}
</ul>
<h2>数字列表(过滤2)</h2>
<ul>
{this.state.numbers.filter((item) => item >= 50)
.map((item) => (<li>{item}</li>))}
</ul>
<h2>数字列表(截取)</h2>
<ul>
{this.state.numbers.slice(0, 4).map((item) => {
return <li>{item}</li>;
})}
</ul>
</div>
);
}
}
key是虚拟DOM对象的标识,,使用列表渲染的时候注意加key可以在更新显示时提升性能
最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
最好不要使用index作为key,因为如果对数据进行逆序添加、逆序删除等破坏顺序操作会产生没有必要的真实DOM更新
仅用于渲染列表用于展示,使用index作为key是没有问题的
class Person extends React.Component {
state = {
persons: [
{ id: 1, name: "小张", age: 18 },
{ id: 2, name: "小李", age: 19 },
],
};
render() {
return (
<div>
<h3>使用id(数据的唯一标识)作为key</h3>
<ul>
{this.state.persons.map((personObj) => {
return (
<li key={personObj.id}>
{personObj.name}---{personObj.age}
<input type="text" />
</li>
);
})}
</ul>
</div>
);
}
}
购物车案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
table {
border: 1px solid #eee;
border-collapse: collapse;
}
th,
td {
border: 1px solid #eee;
padding: 10px 16px;
text-align: center;
}
th {
background-color: #ccc;
}
.count{
margin: 0 5px;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="../react/react.development.js"></script>
<script src="../react/react-dom.development.js"></script>
<script src="../react/babel.min.js"></script>
<script src="./format-utils.js"></script>
<script type="text/babel">
class App extends React.Component {
state = {
books: [
{
id: 1,
name: "《算法导论》",
date: "2006-9",
price: 85.0,
count: 2,
},
{
id: 2,
name: "《UNIX编程艺术》",
date: "2006-2",
price: 59.0,
count: 1,
},
{
id: 3,
name: "《编程珠玑》",
date: "2008-10",
price: 39.0,
count: 1,
},
{
id: 4,
name: "《代码大全》",
date: "2006-3",
price: 128.0,
count: 1,
},
],
};
renderBook() {
return (
<div>
<table>
<thead>
<tr>
<th></th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{this.state.books.map((item, index) => {
return (
<tr>
<td>{index + 1}</td>
<td>{item.name}</td>
<td>{item.date}</td>
<td>{item.price}</td>
<td>
<button onClick={() => this.changeCount(index, -1)}>
-
</button>
<span className="count">{item.count}</span>
<button onClick={() => this.changeCount(index, 1)}>
+
</button>
</td>
<td onClick={(e) => this.removeBook(e, index)}>
移除
</td>
</tr>
);
})}
</tbody>
</table>
<h2>总价格{this.totoalPrice()}</h2>
</div>
);
}
renderEmptyBook() {
return <h2>购物车已被清空</h2>;
}
render() {
return this.state.books.length
? this.renderBook()
: this.renderEmptyBook();
}
totoalPrice() {
const total = this.state.books.reduce((preVal, item) => {
return preVal + item.count * item.price;
}, 0);
return formatPrice(total);
}
removeBook(e, index) {
const changeBook = this.state.books.filter(
(item, indey) => index !== indey
);
this.setState({
books: changeBook,
});
}
changeCount(index, count) {
const newBookArr = [...this.state.books];
newBookArr[index].count += count;
if (newBookArr[index].count <= 0) newBookArr[index].count = 0;
this.setState({
books: newBookArr,
});
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
</body>
</html>
二、生命周期
React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用
我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作
页面加载时使文字透明度不断递减恢复
//生命周期回调函数 <=> 生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子
class Life extends React.Component{
state = {opacity:1}
death = () => {
//卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
//组件挂完毕
componentDidMount(){
console.log('componentDidMount');
this.timer = setInterval(() => {
//获取原状态
let {opacity} = this.state
//减小0.1
opacity -= 0.1
if(opacity <= 0) opacity = 1
//设置新的透明度
this.setState({opacity})
}, 200);
}
//组件将要卸载
componentWillUnmount(){
//清除定时器
clearInterval(this.timer)
}
//初始化渲染、状态更新之后
render(){
console.log('render');
return(
<div>
<h2 style={{opacity:this.state.opacity}}>React学不会怎么办?</h2>
<button onClick={this.death}>不活了</button>
</div>
)
}
}
React 生命周期主要包括三个阶段:初始化阶段,更新阶段,销毁阶段
-
初始化阶段: 由ReactDOM.render()触发---初次渲染
- constructor()
- getDerivedStateFromProps()
- render()
- componentDidMount() 常用,一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
-
更新阶段: 由组件内部this.setSate()或父组件重新render触发
- getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate()
-
卸载组件: 由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount() 常用,一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
class Count extends React.Component {
constructor(props) {
console.log("Count---constructor");
super(props);
//初始化状态
this.state = { count: 0 };
}
//加1按钮的回调
add = () => {
//获取原状态
const { count } = this.state;
//更新状态
this.setState({ count: count + 1 });
};
//卸载组件按钮的回调
death = () => {
ReactDOM.unmountComponentAtNode(document.getElementById("test"));
};
//强制更新按钮的回调
force = () => {
this.forceUpdate();
};
// 若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
static getDerivedStateFromProps(props, state) {
console.log("getDerivedStateFromProps", props, state);
return null;
}
//在更新之前获取快照
getSnapshotBeforeUpdate() {
console.log("getSnapshotBeforeUpdate");
return "yunmu";
}
//组件挂载完毕的钩子
componentDidMount() {
console.log("Count---componentDidMount");
}
//组件将要卸载的钩子
componentWillUnmount() {
console.log("Count---componentWillUnmount");
}
//控制组件更新的“阀门”
shouldComponentUpdate() {
console.log("Count---shouldComponentUpdate");
return true;
}
//组件更新完毕的钩子
componentDidUpdate(preProps, preState, snapshotValue) {
console.log(
"Count---componentDidUpdate",
preProps,
preState,
snapshotValue
);
}
render() {
console.log("Count---render");
const { count } = this.state;
return (
<div>
<h2>当前求和为:{count}</h2>
<button onClick={this.add}>点我+1</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>
不更改任何状态中的数据,强制更新一下
</button>
</div>
);
}
}
getSnapshotBeforeUpdate的使用场景
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>4_getSnapShotBeforeUpdate的使用场景</title>
<style>
.list {
width: 200px;
height: 150px;
background-color: skyblue;
overflow: auto;
}
.news {
height: 30px;
}
</style>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>
<script type="text/babel">
class NewsList extends React.Component {
state = { newsArr: [] };
componentDidMount() {
setInterval(() => {
//获取原状态
const { newsArr } = this.state;
//模拟一条新闻
const news = "新闻" + (newsArr.length + 1);
//更新状态
this.setState({ newsArr: [news, ...newsArr] });
}, 1000);
}
getSnapshotBeforeUpdate() {
return this.refs.list.scrollHeight;
}
componentDidUpdate(preProps, preState, SnapshotHeight) {
this.refs.list.scrollTop += this.refs.list.scrollHeight - SnapshotHeight;
}
render() {
return (
<div className="list" ref="list">
{this.state.newsArr.map((n, index) => {
return (
<div key={index} className="news">
{n}
</div>
);
})}
</div>
);
}
}
ReactDOM.render(<NewsList />, document.getElementById("test"));
</script>
</body>
</html>