动态地向 React map 函数中的前 n 个元素添加 class
Adding a class to first n elements in React map function dynamically
我正在通过一个数组进行映射,我想为其分配一个 class 到前 n 个元素。前n个元素由父组件传递,并在稳步递增。
我可以使用像 className={index >= firstNElements ? '' : 'MyClass';}
这样的三元运算符,但这需要映射所有数组项。在数组中有几千个项目并且 prop 频繁变化的情况下,这似乎不太有效。有没有更快的方法来完成这项任务?比如为索引小于 firstNElements 的所有元素构造一个 while 循环?
import React from "react";
export const myComponent = ({ firstNElements, myArray }) => {
return(
<div>
{myArray.map((arrayItem) => (
<span key={arrayItem.key}>{arrayItem.content}</span>
))}
</div>
);
}
I could use something like a ternary operator like className={index >= firstNElements ? '' : 'MyClass';}
这就是你的做法(但没有 ;
,那个 JSX 表达式的内容是一个 表达式 ,而不是一个语句)
...however this would require all array items to be mapped through.
无论您是否也这样做,只要呈现组件,您就可以这样做。 “几千”在 map
或创建跨度方面不太可能成为问题(更可能是 DOM 渲染问题)。
如果你的键是一致的,span的细节没有改变(和上次一样class,和上次一样的内容等等),React会留下等价的span
仅在 DOM 中。如果渲染后 DOM 没有改变,它不会更新或替换它。
您的组件看起来很简单。因为它总是为相同的道具创建相同的输出,你可以在它上面使用 React.memo
。来自文档:
If your function component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.
请注意,这里的“渲染”意味着调用您的组件函数来为其获取 React 元素,而不是 DOM 渲染。
在上面使用 memo
看起来像这样:
export const myComponent = React.memo(({ firstNElements, myArray }) => {
return(
<div>
{myArray.map((arrayItem) => (
<span key={arrayItem.key}>{arrayItem.content}</span>
))}
</div>
);
});
如果您的组件比显示的更复杂,跨度只是其中的一部分,和你发现一个性能问题你追溯到map
(而不是DOM渲染等),你可以memoize一组跨度使用 useMemo
所以你不需要使用 map
除非跨度发生变化,但我不会那样做 until/unless 你已经将特定问题追溯到 map
调用自己。
FWIW,这是有条件表达式和没有条件表达式的简单比较:
// Why have warmup? The first time through triggers JIT, which makes the first
// run slower. So if I put with first, it's slower than without; if I put without
// first, it's slower than with. After the first time each has run, though,
// the JIT has *probably* done its work and we're looking at what you'll
// get from that point forward.
const MyComponentWith = ({ firstNElements, myArray, warmup = false }) => {
if (!warmup) {
console.time("with");
}
const spans = myArray.map((arrayItem, index) => (
<span className={index >= firstNElements ? "" : "the-class"} key={arrayItem.key}>{arrayItem.content}</span>
))
if (!warmup) {
console.timeEnd("with");
}
return(
<div>
{spans}
</div>
);
};
const MyComponentWithout = ({ firstNElements, myArray, warmup = false }) => {
if (!warmup) {
console.time("without");
}
const spans = myArray.map((arrayItem) => (
<span key={arrayItem.key}>{arrayItem.content}</span>
))
if (!warmup) {
console.timeEnd("without");
}
return(
<div>
{spans}
</div>
);
};
const items = Array.from(Array(30000), (_, i) => ({
key: i,
content: `Span #${i} `
}));
const first = 200;
ReactDOM.render(
<div>
<MyComponentWithout warmup={true} firstNElements={first} myArray={items} />
<MyComponentWith warmup={true} firstNElements={first} myArray={items} />
<MyComponentWithout firstNElements={first} myArray={items} />
<MyComponentWith firstNElements={first} myArray={items} />
<MyComponentWithout firstNElements={first} myArray={items} />
<MyComponentWith firstNElements={first} myArray={items} />
<MyComponentWithout firstNElements={first} myArray={items} />
<MyComponentWith firstNElements={first} myArray={items} />
</div>,
document.getElementById("root")
);
.the-class {
color: green;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
我得到的结果(勇敢;基本上 Chrome):
without: 4.940ms
with: 7.470ms
without: 3.480ms
with: 5.460ms
without: 9.180ms
with: 11.010ms
条件表达式似乎花费了大约 2-3 毫秒(在我的设置中,使用那个相当幼稚的测试)。
切片数组
你可以把它切成两半,对上半部分做你需要做的。同样使用 slice 不会改变原始数组值。突变可能非常糟糕,尤其是在 react.I 中回答了为什么 here
import React from "react";
export const myComponent = ({ firstNElements, myArray }) => {
let arrayFirstPart = yourArray.slice(0, firstNElements);
let arraySecondPart = yourArray.slice(firstNElements, myArray.length);
return(
<div>
{arrayFirstPart.map((arrayItem) => (
<span key={arrayItem.key} className="fist-set-of-values">{arrayItem.content}</span>
))}
{arraySecondPart.map((arrayItem) => (
<span key={arrayItem.key}>{arrayItem.content}</span>
))}
</div>
);
}
正如您在问题中明确提到的,要走的路是使用
className={index >= firstNElements ? '' : 'MyClass'}
然而,因为您已经在项目之间进行映射,所以只剩下您将第二个参数传递给 map 函数,这将是索引,然后您可以将它与 firstNElements 进行比较,基本上如下所示
import React from "react";
export const myComponent = ({ firstNElements, myArray }) => {
return(
<div>
{myArray.map((arrayItem, index) => (
<span key={arrayItem.key} className={index >= firstNElements ? '' : 'MyClass'}>{arrayItem.content}</span>
))}
</div>
);
}
React 将根据 props 中已知的内容变化来决定重新挂载哪个 dom 元素。
我正在通过一个数组进行映射,我想为其分配一个 class 到前 n 个元素。前n个元素由父组件传递,并在稳步递增。
我可以使用像 className={index >= firstNElements ? '' : 'MyClass';}
这样的三元运算符,但这需要映射所有数组项。在数组中有几千个项目并且 prop 频繁变化的情况下,这似乎不太有效。有没有更快的方法来完成这项任务?比如为索引小于 firstNElements 的所有元素构造一个 while 循环?
import React from "react";
export const myComponent = ({ firstNElements, myArray }) => {
return(
<div>
{myArray.map((arrayItem) => (
<span key={arrayItem.key}>{arrayItem.content}</span>
))}
</div>
);
}
I could use something like a ternary operator like
className={index >= firstNElements ? '' : 'MyClass';}
这就是你的做法(但没有 ;
,那个 JSX 表达式的内容是一个 表达式 ,而不是一个语句)
...however this would require all array items to be mapped through.
无论您是否也这样做,只要呈现组件,您就可以这样做。 “几千”在 map
或创建跨度方面不太可能成为问题(更可能是 DOM 渲染问题)。
如果你的键是一致的,span的细节没有改变(和上次一样class,和上次一样的内容等等),React会留下等价的span
仅在 DOM 中。如果渲染后 DOM 没有改变,它不会更新或替换它。
您的组件看起来很简单。因为它总是为相同的道具创建相同的输出,你可以在它上面使用 React.memo
。来自文档:
If your function component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.
请注意,这里的“渲染”意味着调用您的组件函数来为其获取 React 元素,而不是 DOM 渲染。
在上面使用 memo
看起来像这样:
export const myComponent = React.memo(({ firstNElements, myArray }) => {
return(
<div>
{myArray.map((arrayItem) => (
<span key={arrayItem.key}>{arrayItem.content}</span>
))}
</div>
);
});
如果您的组件比显示的更复杂,跨度只是其中的一部分,和你发现一个性能问题你追溯到map
(而不是DOM渲染等),你可以memoize一组跨度使用 useMemo
所以你不需要使用 map
除非跨度发生变化,但我不会那样做 until/unless 你已经将特定问题追溯到 map
调用自己。
FWIW,这是有条件表达式和没有条件表达式的简单比较:
// Why have warmup? The first time through triggers JIT, which makes the first
// run slower. So if I put with first, it's slower than without; if I put without
// first, it's slower than with. After the first time each has run, though,
// the JIT has *probably* done its work and we're looking at what you'll
// get from that point forward.
const MyComponentWith = ({ firstNElements, myArray, warmup = false }) => {
if (!warmup) {
console.time("with");
}
const spans = myArray.map((arrayItem, index) => (
<span className={index >= firstNElements ? "" : "the-class"} key={arrayItem.key}>{arrayItem.content}</span>
))
if (!warmup) {
console.timeEnd("with");
}
return(
<div>
{spans}
</div>
);
};
const MyComponentWithout = ({ firstNElements, myArray, warmup = false }) => {
if (!warmup) {
console.time("without");
}
const spans = myArray.map((arrayItem) => (
<span key={arrayItem.key}>{arrayItem.content}</span>
))
if (!warmup) {
console.timeEnd("without");
}
return(
<div>
{spans}
</div>
);
};
const items = Array.from(Array(30000), (_, i) => ({
key: i,
content: `Span #${i} `
}));
const first = 200;
ReactDOM.render(
<div>
<MyComponentWithout warmup={true} firstNElements={first} myArray={items} />
<MyComponentWith warmup={true} firstNElements={first} myArray={items} />
<MyComponentWithout firstNElements={first} myArray={items} />
<MyComponentWith firstNElements={first} myArray={items} />
<MyComponentWithout firstNElements={first} myArray={items} />
<MyComponentWith firstNElements={first} myArray={items} />
<MyComponentWithout firstNElements={first} myArray={items} />
<MyComponentWith firstNElements={first} myArray={items} />
</div>,
document.getElementById("root")
);
.the-class {
color: green;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>
我得到的结果(勇敢;基本上 Chrome):
without: 4.940ms with: 7.470ms without: 3.480ms with: 5.460ms without: 9.180ms with: 11.010ms
条件表达式似乎花费了大约 2-3 毫秒(在我的设置中,使用那个相当幼稚的测试)。
切片数组
你可以把它切成两半,对上半部分做你需要做的。同样使用 slice 不会改变原始数组值。突变可能非常糟糕,尤其是在 react.I 中回答了为什么 here
import React from "react";
export const myComponent = ({ firstNElements, myArray }) => {
let arrayFirstPart = yourArray.slice(0, firstNElements);
let arraySecondPart = yourArray.slice(firstNElements, myArray.length);
return(
<div>
{arrayFirstPart.map((arrayItem) => (
<span key={arrayItem.key} className="fist-set-of-values">{arrayItem.content}</span>
))}
{arraySecondPart.map((arrayItem) => (
<span key={arrayItem.key}>{arrayItem.content}</span>
))}
</div>
);
}
正如您在问题中明确提到的,要走的路是使用
className={index >= firstNElements ? '' : 'MyClass'}
然而,因为您已经在项目之间进行映射,所以只剩下您将第二个参数传递给 map 函数,这将是索引,然后您可以将它与 firstNElements 进行比较,基本上如下所示
import React from "react";
export const myComponent = ({ firstNElements, myArray }) => {
return(
<div>
{myArray.map((arrayItem, index) => (
<span key={arrayItem.key} className={index >= firstNElements ? '' : 'MyClass'}>{arrayItem.content}</span>
))}
</div>
);
}
React 将根据 props 中已知的内容变化来决定重新挂载哪个 dom 元素。