壹 ❀ 引

从零开始的react入门教程(三),了解react事件与使用注意项一文中,我们了解了react中事件命名规则,绑定事件时对于this的处理,以及事件中可使用的e对象。那么这篇文章中我们来熟悉react中常用的条件渲染语法。

贰 ❀ 条件渲染

在开发中,我们常有根据一个变量值的真或假来决定渲染A或者B内容的情况,这种需求不管用三元或者if语句都能轻松实现,比如实现一个简单的登录是否成功的文案提示功能:

class IsLogin extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
isLogin: false
}
}
handleLogin = () => {
this.setState({ isLogin: true });
}
handleLogout = () => {
this.setState({ isLogin: false });
}
renderLogin = () => {
return <h1>欢迎回来!</h1>
}
renderLogout = () => {
return <h1>你好,请登录!</h1>
}
render() {
return (
<div className="isLogin">
<div>{this.state.isLogin ? this.renderLogin() : this.renderLogout()}</div>
<button className="login" onClick={this.handleLogin}>login</button>
<button className="logout" onClick={this.handleLogout}>logout</button>
</div>
);
}
}; ReactDOM.render(<IsLogin />, document.getElementById('root'));

在这个例子中,我们可以通过两个按钮改变state中关于isLogin的值,而这个值又决定了最终在render中应该渲染哪个文案。通过isLogin的变化,我们甚至都不需要同时显示两个按钮,同样通过变量的变化来决定渲染哪个按钮,修改render为如下,那么效果就是这样了:

render() {
const buttonType = this.state.isLogin
? <button className="logout" onClick={this.handleLogout}>logout</button>
: <button className="login" onClick={this.handleLogin}>login</button>
return (
<div className="isLogin">
<div>{this.state.isLogin ? this.renderLogin() : this.renderLogout()}</div>
{buttonType}
</div>
);
}

在react的花括号中,我们还能用JS逻辑运算符玩一些花样,比如前面的例子是根据情况显示A或者B,现在我们希望要么显示A,要么什么都不显示,这里就可以用逻辑运算符&&,其实效果与满足if条件完全一致,比如这个官网提供的例子:

function UnreadMessage(props) {
const unreadMessage = props.msg;
return (
<div className="unreadMessage">
<h1>你好!</h1>
{unreadMessage.length > 0 &&
<h2>
你有{unreadMessage.length}条未读信息。
</h2>
}
</div>
)
}
const emailMsg = [1, 2, 3];
ReactDOM.render(<UnreadMessage msg={emailMsg} />, document.getElementById('root'));

在这个例子中就凸显出了JSX语法的特点,我们将JS的逻辑判断与react元素糅合在了一起,并由{}去提供解析。站在JS的角度,这里所做的其实就是下面这段代码:

const emailMsg = [1, 2, 3];
let unreadMessageText = '';
if(unreadMessage.length){
unreadMessageText = `你有${emailMsg.length}条未读信息。`
}

逻辑运算符除了&&之外还有||,这两个的区别简单介绍下,首先是A&&B,它的执行为当执行到A为真时才会继续执行到后面的B,看个例子:

function A(bool) {
return bool;
};
function B() {
console.log(1);
};
A(true) && B();//B输出1
A(false) && B();//B不会执行

A||B的意思是当A为真时就不会继续执行B了,因为只要有一个为真就可以了,所以当A为假时才会跑后面的B。

A(true) || B();//B不会执行
A(false) || B();//B输出1

对于||的场景,比较常见的是程序中需要传递某个值,假设前者为假,我们会提供一个默认值传递,保证后续的逻辑不会出错:

const arr = props.arr || [];
arr.filter(ele => ele);

在前面的例子中,我们都是根据条件决定组件内部渲染什么,还有种可能性,就是在特定情况下,我们希望组件内部什么都不要渲染;虽然这个组件有被调用,但不管是函数组件还是class组件,都需要通过return来返回需要渲染的react元素,所以在特定条件下,我们可以在return元素前直接return null来达到目的。

function IsShow(props) {
if(!props.show){
return null;
}
return (
<div >hello!</div>
)
} ReactDOM.render(<IsShow show={false}/>, document.getElementById('root'));

这个例子中,虽然组件IsShow有被调用,但因为组件并未返回任何dom,所以在界面上我们看不到任何东西。

那么到这里我们介绍了react中一些常见的条件渲染场景,在{}中你可以根据需要任何组合这些条件并拿到自己想要的最终结果。

叁 ❀ 列表渲染

在实际开发中,我们常有将数组类数据渲染成列表的需求,在vue或者或者小程序中我们可以借用指令来达到目的,比如vue中的v-for,小程序中的wx:forangularjs中的的ng-repeat等,以vue为例遍历一个数组可以这样做:

const app = new Vue({
el: '#list',
data: {
users: [
{ name: '听风' },
{ name: '是风' }
]
}
});
<ul id="list">
<!-- 利用v-for遍历 -->
<li v-for="user in users" :key="user.name">
{{ user.name }}
</li>
</ul>

但我们在react中的列表渲染会有所不同,我们不会借用类似的指令,而是通过数组API直接遍历数据并得到我们想要的react元素块,再加入render中进行解析渲染。

在JS中我们想要将一个数组中所有的元素都乘以2可以这么做:

const doubled = [1,2,3].map(ele => ele*2);// [2, 4, 6]

而react遍历列表也类似如此,比如我们需要在ul中通过li展示上面这些结果,我们则需要将要展示的所有li都提前遍历出来,再作为一个变量赋予给ul,像这样:

function List(props) {
const list = props.nums.map(ele => (
<li>{ele * 2}</li>
));
return <ul>{list}</ul>
}
const nums = [1, 2, 3];
ReactDOM.render(<List nums={nums} />, document.getElementById('root'));

在这个例子中,我们先通过map遍历,得到了包含多个li标签的合集,并保存在了变量list中,之后又将list赋予给ul标签内部,从而实现了我们想要的效果。看似完美的效果,当打开控制台就不那么完美了,这段代码报给出了红色警告:

list中的每个child都应该有一个独一无二的属性作为key。这个问题我想大家在vue或者小程序中都有类似的处理,我们来看看react如何解决。

肆 ❀ 独一无二的key

肆 ❀ 壹 为什么要用key

为什么要添加key?我想大家应该都有听说diff算法,对于react而言,每次的props或者state修改都会触发render重新渲染视图,如果是完整的重新渲染代价是昂贵的,而添加key的目的是便于react在数据修改后,能记录元素知道它对应的是先前的谁并进行对比,比如我们有个数组[0,1,2]被渲染,之后被修改为[0,2,2],对于react而言,它只要找到第二个li并修改它的渲染内容即可,而不是完整去渲染。

所以回到上面的列表渲染的例子,我们可以这样为li添加key属性:

const list = props.nums.map(ele => (
<li key={ele}>{ele * 2}</li>
));

我们直接将数组遍历的每个元素自身作为key赋予给了li,保存代码,你会发现控制台的警告已经没有了。

肆 ❀ 贰 不推荐使用index作为key

你也许在想,为什么不用index作为key呢?像这样:

const list = props.nums.map((ele, index) => (
<li key={index}>{ele * 2}</li>
));

但用index做为key其实是有风险的,我们来看个由官网改写的例子:

class Item extends React.Component {
render() {
return (
<div>
<label>{this.props.name}</label>
<div>
<input type='text' />
</div>
</div>
)
}
} class Example extends React.Component {
constructor() {
super();
this.state = {
list: [
{ name: '听风是风', id: 1 },
{ name: '行星飞行', id: 2 }
]
};
} addItem = () => {
const id = +new Date;
this.setState({
list: [{ name: '时间跳跃' + id + id, id }, ...this.state.list]
});
} render() {
return (
<div className="example">
<button onClick={this.addItem}>clie me</button>
<div className="form">
<form>
<h3>不好的做法 <code>key=index</code></h3>
{this.state.list.map((todo, index) =>
<Item {...todo}
key={index} />
)}
</form>
<form>
<h3>更好的做法 <code>key=id</code></h3>
{this.state.list.map((todo) =>
<Item {...todo}
key={todo.id} />
)}
</form>
</div> </div>
)
}
}
ReactDOM.render(<Example />, document.getElementById('root'))

当我们提前为input输入了值,并点击按钮新建输入框时效果就很明显了,我们的本意是在现有输入框头部插入新的输入框。但当使用inde作为key时react对比了新旧index为0的input,由于index前后都是0,所以react认为此时的item组件是可以复用的,它并没有完全替换掉它,而是单纯更新了item内部的label标签,所以你会发现input创建出来是有值的。

而当我们使用第一无二的标识作为key时点击创建,由于前后根本不是一个东西,react选择了重新创建一个全新的lable与input,并插入到了现有DOM节点之前。

通常来说,我们始终不推荐使用index作为key,因为使用key可能在如下场景引发问题:

  • 若对数据进行逆序添加,逆序删除等破坏性操作,会产生没必要的真实dom更新。
  • 如果结构中含包含了输入类的dom,可能会导致react认为这些输入dom没变化,从而引发界面出现信息对不上的问题。

但如果你说我的数据就是没id,这可怎么办,在react官网介绍的博客中,也推荐了用于随机生成id的小工具,例如shortid或者Nano ID,有兴趣大家可以自己看看用法。

肆 ❀ key与组件

在上一个介绍index作为key会造成问题的例子中,不知道大家有没有发现key是写在需要遍历的组件Item上,而非item内部的div上,其实不难理解,对于react而言,组件Item就是一个整体,我们希望这个整体带有唯一标识,在数据变化时,当前的Item是否应该更新或是新建,所以下面这样的写法就是错误的:

function ListItem(props) {
const value = props.value;
return (
// 错误!你不需要在这里指定 key:
<li key={value.toString()}>
{value}
</li>
);
} function List(props) {
const listItems = [1, 2, 3].map((number) =>
// 错误!元素的 key 应该在这里指定:
<ListItem value={number} />
);
return (
<ul>
{listItems}
</ul>
);
} ReactDOM.render(<List />, document.getElementById('root'))

一个规则就是,key永远加在你所用的数组API内部的元素上,修改成如下这样就好了:

function ListItem(props) {
const value = props.value;
return (
<li>
{value}
</li>
);
} function List(props) {
const listItems = [1, 2, 3].map((number) =>
<ListItem value={number} key={number.toString()}/>
);
return (
<ul>
{listItems}
</ul>
);
}

关于key最后一点说明就是,虽然我们说key应该独一无二,但并不是说它在全局是独一无二,而是只针对于兄弟元素之前,在我们前面展示index作为key的例子中,其实我们也将数组给了form中去遍历,由于不是兄弟关系,你会发现它们之间的key就算重名也没任何关系。

伍 ❀ 总

好了,那么到这里我们介绍了react中几种常见的条件渲染用法,其实总结来说,在react的{}中我们能做到很多JS中的条件判断骚操作。

除了条件渲染,我们还介绍了列表渲染,这才开发中将非常普遍,与常规框架不同,react并未提供对应的指令,而是借用数组API直接渲染react元素,而说到列表渲染总是离不开与之配对的key,我们了解了为什么要提供key,以及使用index作为key可能造成的问题,所以在开发中总是建议不要使用index作为key。以上知识就是本文阐述的几个核心点了,时间也不早了,那么到这里本文结束,晚安。

从零开始的react入门教程(四),了解常用的条件渲染、列表渲染与独一无二的key的更多相关文章

  1. 无废话ExtJs 入门教程四[表单:FormPanel]

    无废话ExtJs 入门教程四[表单:FormPanel] extjs技术交流,欢迎加群(201926085) 继上一节内容,我们在窗体里加了个表单.如下所示代码区的第28行位置,items:form. ...

  2. react 入门教程 阮一峰老师真的是榜样

    -  转自阮一峰老师博客 React 入门实例教程   作者: 阮一峰 日期: 2015年3月31日 现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Nati ...

  3. React入门教程(二)

    前言 距离上次我写 React 入门教程已经快2个月了,年头年尾总是比较忙哈,在React 入门教程(一)我大概介绍了 React 的使用和一些注意事项,这次让我们来继续学习 React 一. Rea ...

  4. PySide——Python图形化界面入门教程(四)

    PySide——Python图形化界面入门教程(四) ——创建自己的信号槽 ——Creating Your Own Signals and Slots 翻译自:http://pythoncentral ...

  5. Elasticsearch入门教程(四):Elasticsearch文档CURD

    原文:Elasticsearch入门教程(四):Elasticsearch文档CURD 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接: ...

  6. RabbitMQ入门教程(四):工作队列(Work Queues)

    原文:RabbitMQ入门教程(四):工作队列(Work Queues) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https:/ ...

  7. React入门教程1---初见面

    React入门教程1---初见面:https://blog.csdn.net/solar_lan/article/details/82799248 React 教程 React 是一个用于构建用户界面 ...

  8. JasperReports入门教程(四):多数据源

    JasperReports入门教程(四):多数据源 背景 在报表使用中,一个页面需要打印多个表格,每个表格分别使用不同的数据源是很常见的一个需求.假如我们现在有一个需求如下:需要在一个报表同时打印所有 ...

  9. WebGL入门教程(四)-webgl颜色

    前面文章: WebGL入门教程(一)-初识webgl WebGL入门教程(二)-webgl绘制三角形 WebGL入门教程(三)-webgl动画 颜色效果图: 操作步骤: 1.创建HTML5 canva ...

  10. Spring Cloud 入门教程(四): 分布式环境下自动发现配置服务

    前一章, 我们的Hello world应用服务,通过配置服务器Config Server获取到了我们配置的hello信息“hello world”. 但自己的配置文件中必须配置config serve ...

随机推荐

  1. 【TouchGFX】visual studio 工程中 SIMULATOR 宏定义位置

  2. NewStarCTF 2023 公开赛道 WEEK4|CRYPTO WP

    RSA Variation II 1.题目信息 提示:"Schmidt Samoa" 附件信息 from secret import flag from Crypto.Util.n ...

  3. mongo环境搭建

    安装 mongoDB mongo下载 添加 MONGOROOT/bin 到环境变量 PATH MONGOROOT是mongo安装目录 启动 mongo 服务启动程序 MONGOROOT/bin/mon ...

  4. 使用阿里canal实现mysql与Elasticsearch增量同步

    一.背景介绍 最近在做一个地理信息相关的项目,需要维护大量的地址描述数据,同时需要提供对数据检索的功能,准备采用Elasticsearch(6.7)实现.那么问题就来了,地址数据需要同时在MySQL和 ...

  5. tikv-ctl的简单学习

    tikv-ctl的简单学习 摘要 最近在学习使用 tidb. 有一个场景,单独使用了tikv作为键值对的数据库. 但是比较不幸.总是出现宕机的情况 因为这个环境是单独使用tikv 二进制进行安装的 没 ...

  6. [转帖]CentOS-7-x86_64-DVD-2009 rpm包列表(centos7.9)

    https://www.cnblogs.com/hiyang/p/14803391.html 文件数 4071 个,共3.8G 复制389-ds-base-1.3.10.2-6.el7.x86_64. ...

  7. 【转帖】PyCharm---Django简单例子--基础1

    https://www.cnblogs.com/kllay/p/7286701.html 环境: python 2.7 Django 1.11.2    查看版本:python -m django - ...

  8. Linux 内核参数

    /proc/sys/net/ipv4: ip_local_port_range:定义了TCP或UDP对目标发起连接所选择的本地端口范围(除ip_local_reserved_ports之外),其定义受 ...

  9. 我们开源了一个轻量的 Web IDE UI 框架

    我们开源了一个轻量的 Web IDE UI 框架 Molecule 一个轻量的 Web IDE UI 框架 简介 Molecule 是一个受 VS Code 启发,使用 React.js 构建的 We ...

  10. css3中的圆角border-radius

    css3的属性应该加上浏览器前缀 不同的浏览器内核不同,因此css的前缀不同. 常见的几种浏览器内核 火狐浏览器 Geoko内核 前缀是 -mox- 谷歌浏览器, Webkit内核 前缀是 -wekb ...