在 React 中迭代数据数组时呈现 JSX 元素的最有效方法
Posted
技术标签:
【中文标题】在 React 中迭代数据数组时呈现 JSX 元素的最有效方法【英文标题】:Most efficient way of rendering JSX elements when iterating on array of data in React 【发布时间】:2019-02-10 02:24:00 【问题描述】:我有一个包含对象的数组。我正在创建此数组的映射以使用 span
组件呈现名称。
let data = ["id": "01", "name": "Hi", "id": "02", "name": "Hello"];
我一直在使用以下两个不同的功能来迭代该对象数组,并使用 map 来呈现 JSX 元素。
功能1:
import React, Component from 'react';
class App extends Component
render()
let data = ["id": "01", "name": "Hi", "id": "02", "name": "Hello"];
const items = data.map((key, i) =>
return <span key=key.id>key.name</span>;
);
return (
<div>
items
</div>
);
export default App;
功能2:
import React, Component from 'react';
class App extends Component
render()
let data = ["id": "01", "name": "Hi", "id": "02", "name": "Hello"];
let rows = [];
data.map((key, i) =>
rows.push(<span key=key.id>key.name</span>);
);
return (
<div>
rows
</div>
);
export default App;
我知道上述两种使用map
和渲染JSX 元素的不同方式。除了这两种之外,还有其他方法可以做到这一点吗?如果有,推荐哪个?
【问题讨论】:
"Functionality2" 在调用.map
时创建一个从未使用过的数组。切换到data.forEach
,这样就不会创建不必要的数组了。
但这仅在分配给参考权时?比如 const array = data.map(如果我错了请纠正我
不,无论您是否将其分配给变量,都会创建数组。在上面的代码中,创建了 Array,然后立即有资格进行垃圾收集,因为没有引用指向它。如果切换到.forEach
,则不会创建额外的Array。
好的,明白了。谢谢。当我们在迭代中渲染 jsx 元素时,您还可以帮助我使用其他渲染方式吗?除了我提到的和下面提到的其他内容
对您的两种方法以及一些变体进行快速的JsPerf 比较。功能 1 是您的最佳选择。
【参考方案1】:
第一种方法更好。
Array.prototype.map
在后台创建一个数组,并在对每个元素应用修改后返回它。 Functionality-1 创建两个数组,而 Functionality-2 创建三个。
Functionality-1 读起来更好。这就是通常编写 React 代码的方式。对于像这样的简单元素,我会保存项目的 const 定义,并将 map 语句放在 JSX 中以便直接返回。
【讨论】:
【参考方案2】:我会这样做
const data = [id: 1, name: 'a', id: 2, name: 'b'];
export default class App extends PureComponent
render()
return (
<div>
data.map(( id, name ) => <span key=id>name</span>)
</div>
);
现在,您的 data
不会在每次渲染时都重新实例化,并且您不必垃圾收集任何不必要的变量声明。
【讨论】:
赞成。我要补充一点,如果您想避免将<span>
元素包装在 <div>
中,您可以使用 React Fragments:reactjs.org/docs/fragments.html【参考方案3】:
您可以使用它来更好地理解
const data = [id: 1, name: 'a', id: 2, name: 'b'];
export default class App extends React.Component
render()
return (
<div>
data.map((data, dataIndex ) => <span key=dataIndex>data.name</span>)
</div>
);
这里可以理解为name属于with属性。
【讨论】:
map-method提供的索引不推荐作为元素键值使用。【参考方案4】:大多数情况下,我遵循以下规则:
创建一个渲染项目的组件
// in some file
export const RenderItems = (data) =>
return data && data.map((d, i) => <span key=d.id>d.name</span>) || null
挂钩 RenderItems
import RenderItems from 'some-file'
class App extends Component
render()
// you may also define data here instead of getting data from props
const data = this.props
return (
<div>
<RenderItems data=data />
</div>
)
在组件中附加数据
const data = ["id": "01", "name": "Hi", "id": "02", "name": "Hello"]
<App data=data />
即使使用第二个代码示例,遵循此规则也不会影响性能。推送数组中的项目并渲染项目。因为,您不是直接在渲染钩子中工作。始终注意渲染钩子不会直接在其中实现任何逻辑。
此外,我不会仅仅为了使用密钥而创建 id
:
const data = ["name": "Hi", "name": "Hello"]
//... and when using index as key
.map((d, i) => <span key='item-'+i>
// or,
.map((d, i) => <span key='item-'+i+'-'+d.name>
请参阅 this post 为什么我在使用索引作为键时遵循此语法。
更新:
如果要避免使用不必要的html标签,可以使用React.Fragment
export const RenderItems = (data) =>
return data &&
data.map(
(d, i) => <React.Fragment key=d.id>d.name</React.Fragment>
) || null
// and when rendering, you just return the component
return <RenderItems data=data />
注意:
-
仅当您没有任何其他属性时,您才能使用
<></>
作为 <React.Fragment></React.Fragment>
的别名。由于我们在其上使用 key 属性,而不是使用它。
查看this 以支持React.Fragment
的短符号。
使用<></>
的示例:
<>d.name</>
这将在不带任何标记的 html 中呈现d.name
的值。当我们专门将现有设计转换为响应应用程序时,这被认为是最好的。或者,可能还有其他情况。比如,我们将显示一个定义列表:
<dl>
<dt></dt>
<dd></dd>
<dt></dt>
<dd></dd>
<dt></dd>
</dl>
而且我们不想附加不必要的html标签,那么使用Fragment会让我们的生活更轻松:
例子:
<>
<dt>d.term</dt>
<dd>d.definition</dd>
</>
最重要的情况是在tr
(TR 组件)中渲染td
元素。如果我们不这样做,那么我们就违反了 HTML 的规则。该组件将无法正确呈现。在反应中,它会抛出一个错误。
更新2:
另外,如果你有long list of props,如下所示:
const
items,
id,
imdbID,
title,
poster,
getMovieInfo,
addToFavorites,
isOpen,
toggleModal,
closeModal,
modalData,
= props
你可以考虑像这样解构:
const items, ...other = props
// and in your component you can use like:
<div modalData=other.modalData>
但是,我个人更喜欢使用第一个示例代码。这是因为在开发过程中,我不需要每次都回顾其他组件或寻找控制台。在给定的示例中,有像 modalData=
这样的键,所以我们很容易维护 modalData=other.modalData
。但是如果需要像<div>modalData</div>
这样的代码呢?那么,你也可以同意我的偏好。
【讨论】:
谢谢。在第一次看到之前,我从未尝试过这样的方式。 很高兴为您提供帮助。 索引适合用作此处的键,但通常被认为是不好的做法。答案没有提到这一点。 @estus 与某个常数值一起使用时,它不会被认为是坏的。【参考方案5】:通常,for
或 while
语句是迭代数组的最有效方式。在非关键位置处理小阵列的方式可以认为是微优化。
map
的使用在 React 组件中是惯用的,因为它足够快并且可以将值作为表达式的一部分返回。
虽然这是一个反模式:
let rows = [];
data.map((key, i) =>
rows.push(<span key=key.id>key.name</span>);
);
map
应该将数组元素映射到其他值(因此得名),而不是迭代数组而不是 forEach
或其他循环。这个问题可以用 ESLint array-callback-return
规则跟踪。
组件是无状态的,不需要是Component
类。它可以是功能组件或PureComponent
类。由于data
是常量,所以不需要在每次渲染时都赋值:
const data = ["id": "01", "name": "Hi", "id": "02", "name": "Hello"];
const App = props => <div>
data.map(( id, name ) => <span key=id>name</span>)
</div>;
【讨论】:
谢谢。如果我想在迭代中生成带有事件处理函数的 jsx 元素怎么办。我还能为此使用无状态组件吗? @Think-Twice 可能是的,这取决于这些处理程序应该做什么。你能用另一个例子来更新这个问题吗?【参考方案6】:第一种方法是正确的。使用 map 函数遍历数组。
export default class App extends React.Component
constructor(props)
super(props);
this.state =
data: ["id": "01", "name": "Hi", "id": "02", "name": "Hello"];
;
render()
return(
<div>
this.state.data.map((data,index)=>
<span key=data.id>data.name</span>
)
);
【讨论】:
【参考方案7】:我会在 return(...)
中使用 map
,因为 map 返回一个数组。它更干净、更易读,这是我个人努力的目标。
为了补充答案,如果数组 data
可能会改变方向,我会创建一个新的无状态转储组件:
const Spanner = props => return <span key= props.data.id > props.data.name </span> ;
class App extends Component
render()
let data = ["id": "01", "name": "Hi", "id": "02", "name": "Hello"];
return (
<div>
data.map( (item, ind) =>
return (<Spanner key=item.id data=item.name />);
)
</div>
);
这样我们就可以把这个 Spanner 组件当作一个公共组件在不同组件之间共享。如果data
随时间变化(大多数情况下会这样),您可以将包装函数写入 Spanner 组件并在需要时调用新函数。
data=["id": "01", "name": "Hi", "userType": "Admin", "id": "02", "name": "Hello", "userType": "Client"];
const UserWidget = (props) =>
return (
<div>
<h4> props.type </h4>
<Spanner key= props.key data= props.name />
</div>
);
// now both the UserWidget and the Spanner Components can be used wherever required
// say a dashboard Component wants to use the UserWidget
class Dashboard extends Component
render()
let data = ["id": "01", "name": "Hi", "userType": "Admin", "id": "02", "name": "Hello", "userType": "Client"];
return (
<div>
data.map( (item, ind) =>
return (<UserWidget type= item.userType key=item.id data=item.name />);
)
</div>
);
这样您就不会重复自己,即使现在新的 data
数组具有新的键 userType
,您的应用程序组件仍然具有预期的行为。
这也是我的做法。
【讨论】:
【参考方案8】:函数toSpan
可以复用。
class App extends React.Component
render()
const data = [id: `01`, name: `Hi`, id: `02`, name: `Hello`]
const toSpan = (id, name) => <span key=id>name</span>
return (
<div>data.map(toSpan)</div>
)
【讨论】:
以上是关于在 React 中迭代数据数组时呈现 JSX 元素的最有效方法的主要内容,如果未能解决你的问题,请参考以下文章