javascript - 通过 ReactJS 中的一个共享字段(整数)对多个不同对象的数组进行排序?
问题描述
我有一个对象(页面),其中包含多个不同的嵌套对象(问题、图像、文本、视频等),它们都具有相同的属性(数字)作为来自 Ruby on Rails 的 JSON
render :json => @page, :include => [:videos, :images, :rtfs, questions: { :include => :answers }]
如何显示按 number 属性排序的所有这些内容?我可以轻松地通过page.images
、page.videos
等进行映射,并以正确的相应顺序显示它们,因为它们已经按 RoR 中的数字字段排序,但这只能让我到目前为止。假设该页面有 2 个问题(编号 2 和 4)、1 个视频(编号 3)、1 个图像(编号 1)并且没有 rtf。现在我只是先映射视频并渲染它们,然后是图像,然后是问题,然后是 rtf。但是在这种特殊情况下我需要做的是首先显示图像(因为它的数字属性是 1),然后是一个问题(数字属性是 2),然后是视频(因为它是数字 3),最后,另一个问题。我觉得我需要为所有嵌套对象类型创建单独的数组(videos, images, questions
等),然后将它们(最有可能使用 splat 运算符)合并到一个名为 的数组page_data
中,按数字字段对其进行排序,然后进行渲染。由于我对编码还是很陌生,所以我很难弄清楚,希望能得到任何帮助!以下是 React 组件的相关部分
class LessonCreatorPage extends Component {constructor(props) {
super(props);
this.state = {
page_preview: {}, //this is the main object that has different types of nested objects
}}
showPage(id){
//returns the response from backend and passes it to page_preview object
}
};
render(){
{this.state.page_preview && (
React.Fragment>
对于 this.state.page_preview.images、questions、rtf,重复以下代码段。它们都以不同的方式显示
{this.state.page_preview.videos && (
<React.Fragment>
{this.state.page_preview.videos.map((video) =>{
const video_id = `${video.vimeo_id}`;
return (
<React.Fragment key={video.id}>
<Label pointing="below">{video.title}</Label>
<Embed id={video_id} source="vimeo" />
<Label pointing>{video.description}</Label>
</React.Fragment>
);
})}
</React.Fragment>
)}
</React.Fragment>
)}
解决方案
我觉得我需要为所有嵌套对象类型(
videos
,images
,questions
等)创建单独的数组,然后将它们(最有可能使用 splat 运算符)合并到一个名为 的数组page_data
中,按数字字段对其进行排序,然后渲染它.
你在正确的道路上。不过,没有理由在服务器上执行此操作。您正在请求 JSON,并且您有用 reactjs标记的问题,所以我假设数据将在那里传递。
您可以在客户端组合不同的项目,根据 排序number
,然后显示它们。无需更改服务器代码。下面的代码片段是如何以所需方式显示数据的示例。
在 JavaScript 中,您可以使用扩展...
运算符(类似于 Ruby 中的 splat 运算符)组合 2 个数组。
const a = [1, 2];
const b = [3, 4];
[...a, ...b]; //=> [1, 2, 3, 4]
然后,您可以使用.sort()
按所需顺序对组合数组进行排序。
<script type="text/babel">
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = { page: null, pageData: null };
}
async componentDidMount() {
// You might want to wrap the code in a try/catch block to handle any errors
// that might occur (like no network connection).
const page = await fetchRailsPage();
// Concatenate the different items into a single array, but add a new type
// property first to differentiate the different types of items later.
const pageData = [
...page.videos.map((video) => ({ ...video, type: "video" })),
...page.images.map((image) => ({ ...image, type: "image" })),
...page.rtfs.map((rtf) => ({ ...rtf, type: "rtf" })),
...page.questions.map((question) => ({ ...question, type: "question" })),
].sort((a, b) => a.number - b.number);
this.setState({ page, pageData });
}
render() {
if (!this.state.page) return <>loading...</>;
return (
<>
<h1>{this.state.page.title}</h1>
{this.state.pageData.map((item) => (
<p key={JSON.stringify([item.type, item.id])}>
{item.number} - {item.title}
</p>
))}
</>
);
}
}
// mock fetching and parsing the JSON data
async function fetchRailsPage() {
await sleep(2000);
return {
id: 1,
title: "page title",
videos: [
{ id: 1, number: 3, title: "video title" },
],
images: [
{ id: 1, number: 1, title: "image title" },
],
rtfs: [],
questions: [
{ id: 1, number: 2, title: "question title #1", answers: [] },
{ id: 2, number: 4, title: "question title #2", answers: [] },
],
};
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
ReactDOM.render(<Demo />, document.querySelector("#demo"));
</script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="demo"></div>
在上面的代码片段.map((video) => ({ ...video, type: "video" }))
中是可选的,但type
为每个项目添加了一个属性。这可以省略,但可能很有用,因为您可能希望渲染与“问题”对象不同的“视频”对象。
type
添加属性用法的一个示例是将map()
回调更改为类似的内容(您也可以使用switch
语句):
{this.state.pageData.map((item) => {
if (item.type == "video") {
const video = item;
const video_id = `${video.vimeo_id}`;
return (
<React.Fragment key={video.id}>
<Label pointing="below">{video.title}</Label>
<Embed id={video_id} source="vimeo" />
<Label pointing>{video.description}</Label>
</React.Fragment>
);
}
if (item.type == "image") {
// ...
}
})}
另一种选择是传递渲染组件或函数而不是类型。
// in your class component
renderVideo(video) {
const video_id = `${video.vimeo_id}`;
return (
<React.Fragment key={video.id}>
<Label pointing="below">{video.title}</Label>
<Embed id={video_id} source="vimeo" />
<Label pointing>{video.description}</Label>
</React.Fragment>
);
}
然后代替类型,传递渲染方法的名称:
...page.videos.map((video) => ({ ...video, render: "renderVideo" })),
最后,更新方法map()
中的回调render
:
{this.state.pageData.map((item) => this[item.render](item)}
推荐阅读
- python - 如何以原始格式从外部文件导入正则表达式且没有额外的转义字符
- python - 使用 Python 从 JavaScript `{ var1, var2, ...}` 等局部变量创建字典
- asp.net-core - 我应该将 Identity Server 4 用于单个 SPA 客户端吗?
- python - 如何在 Django 中为表单字段添加自定义标题?
- reactjs - 可以运行 npm run build 到 React App 或 ng build --prod 到 Angular 应用程序 Inject Clickjacking 吗?
- python - 如何在模块中导入包
- python - 如何在 python 中使用 if / then 命令
- c++ - 关于函数名查找的困惑
- sql - 生成具有一系列时间戳的表 - Oracle SQL
- c# - 从数组中查找对应的单词以匹配另一个数组中的单词