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

react

在从零开始的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 >

<!-- 利用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 的全部内容, 来源链接: utcz.com/z/382830.html

回到顶部