在 React 中迭代数据数组时呈现 JSX 元素的最有效方式
Most efficient way of rendering JSX elements when iterating on array of data in React
我有一个包含对象的数组。我正在创建此数组的映射以使用 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 元素的不同方式。除了这两种之外,还有其他方法可以做同样的事情吗?如果可以,推荐哪个?
第一种方法更好
Array.prototype.map
在幕后创建一个数组,并在对每个元素应用修改后 returns 它。 Functionality-1 创建两个数组,而 Functionality-2 创建三个数组。
Functionality-1 读起来更好。这就是通常编写 React 代码的方式。对于像这样的简单元素,我会保存项目的 const 定义 并将 map 语句放在要返回的 JSX 中 直接.
我会这样做
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
不会在每次渲染时都重新实例化,您也不必对任何不必要的变量声明进行垃圾回收。
您可以使用它来更好地理解
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属性
大多数情况下,我遵循这个规则:
创建一个呈现项目的组件
// 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}>
请参阅 为什么我在使用索引作为键时遵循此语法。
更新:
如果你想避免使用不必要的 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>
的别名,前提是您没有任何其他 属性。因为我们在上面使用了密钥属性,所以没有使用它。
- 查看 this 以支持
React.Fragment
的缩写。
示例使用 <></>
:
<>{d.name}</>
这将在 html 中呈现 d.name
的值而不带任何标记。当我们专门将现有设计转换为 React 应用程序时,这被认为是最好的。或者,可能还有其他情况。比如,我们要显示一个定义列表:
<dl>
<dt></dt>
<dd></dd>
<dt></dt>
<dd></dd>
<dt></dd>
</dl>
而且我们不想附加不必要的html标签,那么使用Fragment会让我们的生活更轻松:
示例:
<>
<dt>{d.term}</dt>
<dd>{d.definition}</dd>
</>
最重要的情况是渲染 tr
中的 td
元素(一个 TR 组件)。如果我们不这样做,那么我们就违反了 HTML 的规则。该组件将无法正确呈现。在反应中,它会抛出一个错误。
更新 2:
此外,如果您 如下所示:
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>
这样的编码呢?那么,你可能也同意我的偏好。
通常,for
或 while
语句是迭代数组的最有效方法。在非关键位置处理小数组的方式可以认为是微优化。
map
的使用在 React 组件中是惯用的,因为它足够快并且可以 return 一个值作为表达式的一部分。
虽然这是一个反模式:
let rows = [];
data.map((key, i) => {
rows.push(<span key={key.id}>{key.name}</span>);
});
map
应该将数组元素映射到其他值(因此得名),而不是迭代数组而不是 forEach
或其他循环。可以使用 ESLint array-callback-return
规则跟踪此问题。
该组件是无状态的,不需要 Component
class。它可以是功能组件或PureComponent
class。由于 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>;
第一种方法是正确的。使用 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>
)}
);
}
}
我会使用 return(...)
中的 map
作为映射 returns 数组。它更清晰易读,这是我个人所追求的。
为了补充答案,如果数组 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 Component当作一个common component,在不同的component之间共享。如果 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
,您的 App 组件仍然具有预期的行为。
同样,这就是我的做法。
函数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>
)
}
}
我有一个包含对象的数组。我正在创建此数组的映射以使用 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 元素的不同方式。除了这两种之外,还有其他方法可以做同样的事情吗?如果可以,推荐哪个?
第一种方法更好
Array.prototype.map
在幕后创建一个数组,并在对每个元素应用修改后 returns 它。 Functionality-1 创建两个数组,而 Functionality-2 创建三个数组。Functionality-1 读起来更好。这就是通常编写 React 代码的方式。对于像这样的简单元素,我会保存项目的 const 定义 并将 map 语句放在要返回的 JSX 中 直接.
我会这样做
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
不会在每次渲染时都重新实例化,您也不必对任何不必要的变量声明进行垃圾回收。
您可以使用它来更好地理解
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属性
大多数情况下,我遵循这个规则:
创建一个呈现项目的组件
// 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}>
请参阅
更新:
如果你想避免使用不必要的 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>
的别名,前提是您没有任何其他 属性。因为我们在上面使用了密钥属性,所以没有使用它。 - 查看 this 以支持
React.Fragment
的缩写。
示例使用 <></>
:
<>{d.name}</>
这将在 html 中呈现 d.name
的值而不带任何标记。当我们专门将现有设计转换为 React 应用程序时,这被认为是最好的。或者,可能还有其他情况。比如,我们要显示一个定义列表:
<dl>
<dt></dt>
<dd></dd>
<dt></dt>
<dd></dd>
<dt></dd>
</dl>
而且我们不想附加不必要的html标签,那么使用Fragment会让我们的生活更轻松:
示例:
<>
<dt>{d.term}</dt>
<dd>{d.definition}</dd>
</>
最重要的情况是渲染 tr
中的 td
元素(一个 TR 组件)。如果我们不这样做,那么我们就违反了 HTML 的规则。该组件将无法正确呈现。在反应中,它会抛出一个错误。
更新 2:
此外,如果您
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>
这样的编码呢?那么,你可能也同意我的偏好。
通常,for
或 while
语句是迭代数组的最有效方法。在非关键位置处理小数组的方式可以认为是微优化。
map
的使用在 React 组件中是惯用的,因为它足够快并且可以 return 一个值作为表达式的一部分。
虽然这是一个反模式:
let rows = [];
data.map((key, i) => {
rows.push(<span key={key.id}>{key.name}</span>);
});
map
应该将数组元素映射到其他值(因此得名),而不是迭代数组而不是 forEach
或其他循环。可以使用 ESLint array-callback-return
规则跟踪此问题。
该组件是无状态的,不需要 Component
class。它可以是功能组件或PureComponent
class。由于 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>;
第一种方法是正确的。使用 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>
)}
);
}
}
我会使用 return(...)
中的 map
作为映射 returns 数组。它更清晰易读,这是我个人所追求的。
为了补充答案,如果数组 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 Component当作一个common component,在不同的component之间共享。如果 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
,您的 App 组件仍然具有预期的行为。
同样,这就是我的做法。
函数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>
)
}
}