I am trying to integrate React-Beautiful-DND and Ant Design table, but I am facing an issue with rows as I drag them. Their style is changing and the whole table jumps. Anyone tried this before? How do I keep the style of dragged row as it was when it was not dragged?
Any ideas will be much appreciated.
Here is a link to full sandbox.
You can see that if you drag any row, the table will jump.
An here is a full code from the link:
import React, { useState } from "react";
import { Table, Row, Col, Card, Empty } from "antd";
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";
import { mutliDragAwareReorder } from "./utils";
import "./style.css";
const entitiesMock = {
tasks: [
{ id: "0", title: "Very long task title" },
{ id: "1", title: "Task 1" },
{ id: "2", title: "Task 2" },
{ id: "3", title: "Task 3" },
{ id: "4", title: "Task 4" },
{ id: "5", title: "Task 5" },
{ id: "6", title: "Task 6" },
{ id: "7", title: "Task 7" },
{ id: "8", title: "Task 8" },
{ id: "9", title: "Task 9" },
{ id: "10", title: "Task 10" },
{ id: "11", title: "Task 11" },
{ id: "12", title: "Task 12" },
{ id: "13", title: "Task 13" },
{ id: "14", title: "Task 14" },
{ id: "15", title: "Task 15" },
{ id: "16", title: "Task 16" },
{ id: "17", title: "Task 17" },
{ id: "18", title: "Task 18" },
{ id: "19", title: "Task 19" }
],
columnIds: ["todo"],
columns: {
todo: {
id: "todo",
title: "To do",
taskIds: [
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12",
"13",
"14",
"15",
"16",
"17",
"18",
"19"
]
},
done: {
id: "done",
title: "Done",
taskIds: []
}
}
};
const COLUMN_ID_DONE = "done";
export const MultiTableDrag = () => {
const [entities, setEntities] = useState(entitiesMock);
const [selectedTaskIds, setSelectedTaskIds] = useState([]);
const [draggingTaskId, setDraggingTaskId] = useState(null);
const tableColumns = [
{
title: "ID",
dataIndex: "id"
},
{
title: "Title",
dataIndex: "title"
}
];
/**
* Droppable table body
*/
const DroppableTableBody = ({ columnId, tasks, ...props }) => {
return (
<Droppable droppableId={columnId}>
{(provided, snapshot) => (
<tbody
ref={provided.innerRef}
{...props}
{...provided.droppableProps}
className={`${props.className} ${
snapshot.isDraggingOver && columnId === COLUMN_ID_DONE
? "is-dragging-over"
: ""
}`}
>
{props.children}
{provided.placeholder}
</tbody>
)}
</Droppable>
);
};
/**
* Draggable table row
*/
const DraggableTableRow = ({ index, record, columnId, tasks, ...props }) => {
if (!tasks.length) {
return (
<tr className="ant-table-placeholder row-item" {...props}>
<td colSpan={tableColumns.length} className="ant-table-cell">
<div className="ant-empty ant-empty-normal">
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
</div>
</td>
</tr>
);
}
const isSelected = selectedTaskIds.some(
(selectedTaskId) => selectedTaskId === record.id
);
const isGhosting =
isSelected && Boolean(draggingTaskId) && draggingTaskId !== record.id;
return (
<Draggable
key={props["data-row-key"]}
draggableId={props["data-row-key"].toString()}
index={index}
>
{(provided, snapshot) => {
return (
<tr
ref={provided.innerRef}
{...props}
{...provided.draggableProps}
{...provided.dragHandleProps}
className={`row-item ${isSelected ? "row-selected" : ""} ${
isGhosting ? "row-ghosting" : ""
} ${snapshot.isDragging ? "row-dragging" : ""}`}
>
{props.children}
</tr>
);
}}
</Draggable>
);
};
/**
* Get tasks
*/
const getTasks = (entities, id) => {
return entities.columns[id].taskIds.map((taskId) =>
entities.tasks.find((item) => item.id === taskId)
);
};
/**
* On before capture
*/
const onBeforeCapture = (start) => {
const draggableId = start.draggableId;
const selected = selectedTaskIds.find((taskId) => taskId === draggableId);
// if dragging an item that is not selected - unselect all items
if (!selected) {
setSelectedTaskIds([]);
}
setDraggingTaskId(draggableId);
};
/**
* On drag end
*/
const onDragEnd = (result) => {
const destination = result.destination;
const source = result.source;
// nothing to do
if (!destination || result.reason === "CANCEL") {
setDraggingTaskId(null);
return;
}
const processed = mutliDragAwareReorder({
entities,
selectedTaskIds,
source,
destination
});
setEntities(processed.entities);
setDraggingTaskId(null);
};
return (
<>
<Card
className={`c-multi-drag-table ${draggingTaskId ? "is-dragging" : ""}`}
>
<div>
selectedTaskIds: {JSON.stringify(selectedTaskIds)}
<br />
draggingTaskId: {JSON.stringify(draggingTaskId)}
</div>
<br />
<DragDropContext
onBeforeCapture={onBeforeCapture}
onDragEnd={onDragEnd}
>
<Row gutter={40}>
{entities.columnIds.map((id) => (
<Col key={id} xs={12}>
<div className="inner-col">
<Row justify="space-between" align="middle">
<h2>{id}</h2>
<span>
{draggingTaskId && selectedTaskIds.length > 0
? selectedTaskIds.length +
" record(s) are being dragged"
: draggingTaskId && selectedTaskIds.length <= 0
? "1 record(s) are being dragged"
: ""}
</span>
</Row>
<Table
dataSource={getTasks(entities, id)}
columns={tableColumns}
rowKey="id"
components={{
body: {
// Custom tbody
wrapper: (val) =>
DroppableTableBody({
columnId: entities.columns[id].id,
tasks: getTasks(entities, id),
...val
}),
// Custom td
row: (val) =>
DraggableTableRow({
tasks: getTasks(entities, id),
...val
})
}
}}
// Set props on per row (td)
onRow={(record, index) => ({
index,
record
})}
/>
</div>
</Col>
))}
</Row>
<br />
</DragDropContext>
</Card>
</>
);
};
Related
If I have this array:
const data = [
{
id: 1,
category: "ladders",
name: "N/A",
price: 0,
},
{
id: 2,
category: "ladders",
name: "Ladder 1",
price: 220,
},
{
id: 3,
category: "ladders",
name: "Ladder 2",
price: 420,
},
{
id: 4,
category: "ladders",
name: "Ladder 3",
price: 515,
},
]
And I have this in my useState hook:
const [selections, setSelections] = useState({
ladders: [2, 4]
});
I'm trying to output in my JSX the values of the objects in data that have a matching 'id' to the values in my 'ladders' property in my useState hook (i.e. 2 and 4). I can do it by manually specifying the array indexes, like so:
{data
.filter((item) => item.id === selections.ladders[0])
.map((item) => {
return (
<li className="item" key={item.id}>
<div className="item__body">
<h3 className="item__name">{item.name}</h3>
</div>
<span className="item__price">£{item.price}</span>
</li>
);
})}
{data
.filter((item) => item.id === selections.ladders[1])
.map((item) => {
return (
<li className="item" key={item.id}>
<div className="item__body">
<h3 className="item__name">{item.name}</h3>
</div>
<span className="item__price">£{item.price}</span>
</li>
);
})}
But how I can refactor this so I don't have to manually specify the 'ladders' array indexes? Any help appreciated.
Just check whether ladders includes the ID:
.filter((item) => selections.ladders.includes(item.id))
You can use Array.prototype.includes.
{data
.filter((item) => selections.ladders.includes(item.id))
.map((item) => {
return (
<li className="item" key={item.id}>
<div className="item__body">
<h3 className="item__name">{item.name}</h3>
</div>
<span className="item__price">£{item.price}</span>
</li>
);
})}
If you can change the structure of data, you can actually change it to an object for a bit more performance.
{
"1": {
"id": 1,
"category": "ladders",
"name": "N/A",
"price": 0
},
"2": {
"id": 2,
"category": "ladders",
"name": "Ladder 1",
"price": 220
},
"3": {
"id": 3,
"category": "ladders",
"name": "Ladder 2",
"price": 420
},
"4": {
"id": 4,
"category": "ladders",
"name": "Ladder 3",
"price": 515
}
}
Then you can do setSelected(prev=>[...prev, item.id]). And for rendering:
{selected
.map((id) => {
return (
<li className="item" key={id}>
<div className="item__body">
<h3 className="item__name">{data[id].name}</h3>
</div>
<span className="item__price">£{data[id].price}</span>
</li>
);
})}
If you don't have access to data, you can use a Set. Checking if an element is in a set is O(1) as compared to O(n) for Array.includes.
I am novice at this. I am trying to save the values form the DataGridPro MUI, I am using a TreeData. How can i save Values of the Cells after i edit them. I watched a video online in by which i was able to understand how it will work on a Single DataTable, But for the Parent one, i dont know how i will be able to save it. I have looked up but i found no method which could be helpful. I am getting the values from the GridColumn and GridRowsProp.
Basically i want to save the values of the children. how can i save them?
Thanks.
import * as React from "react";
import { DataGridPro, GridColumns, GridRowsProp, DataGridProProps } from "#mui/x-data-grid-
pro";
import {
GridCellEditCommitParams,
GridCellEditStopParams,
GridCellEditStopReasons,
GridRowModel,
MuiEvent,
} from "#mui/x-data-grid";
import { applyInitialState } from "#mui/x-data-grid/hooks/features/columns/gridColumnsUtils";
const rows: GridRowsProp = [
{
hierarchy: ["Documents"],
rubrics: "Head of Human Resources",
totalMarks: "",
id: 0,
},
{
hierarchy: ["Scope"],
rubrics: "Head of Sales",
totalMarks: "15",
id: 1,
},
{
hierarchy: ["Scope", "Rubric item 1"],
rubrics: "Sales Person",
totalMarks: "2",
id: 2,
},
{
hierarchy: ["Scope", "Rubric item 2"],
rubrics: "Sales Person",
totalMarks: "5",
id: 3,
},
{
hierarchy: ["Scope", "Rubric item 3"],
rubrics: "Sales Person",
totalMarks: "8",
id: 4,
},
];
const columns: GridColumns = [
{ field: "rubrics", headerName: "Rubric Type", width: 200, editable: true },
{
field: "totalMarks",
headerName: "Total Marks",
width: 150,
editable: true,
},
];
const getTreeDataPath: DataGridProProps["getTreeDataPath"] = (row) => row.hierarchy;
export default function TreeDataSimple() {
const [state, setState] = React.useState<GridRowModel[]>(applyInitialState); //i was seeing online for this how can i add the array here but no luck.
const handleCommit = (e: GridCellEditCommitParams) => {
const array = state.map((r) => {
if (r.id === e.id) {
return { ...r, [e.field]: e.value };
} else {
return { ...r };
}
});
setState(array);
};
return (
<div style={{ height: 400, width: "100%" }}>
<DataGridPro
treeData
rows={rows}
columns={columns}
getTreeDataPath={getTreeDataPath}
onCellEditCommit={handleCommit}
experimentalFeatures={{ newEditingApi: true }}
onCellEditStop={(params: GridCellEditStopParams, event: MuiEvent) => {
if (params.reason === GridCellEditStopReasons.cellFocusOut) {
event.defaultMuiPrevented = true;
}
}}
/>
</div>
);
}}
I made tabs dynamically from the array list, and there are tabs under the tabs.
Here is the live sanbox link: https://phs69.csb.app/
sandbox code and preview: https://codesandbox.io/s/black-glade-phs69
I have made 3 main tabs and other sub tabs under those 3 main tabs.
I want to keep the tabs state persistant which are under the main tab.
but I am not able to do that.
Here is the entire code of what I have made so far.
import "./styles.css";
import React, { useState } from "react";
const productTypesSizeAttributesList = [
[
{
product_type_id: "1",
attribute_id: "1",
attribute_name: "Achara Cloth",
product_type_method_id: 3,
product_type_type_id: 3,
product_type_unit_id: 4,
attribute_gif_url: "https://i.imgur.com/cr0LVC7.jpg",
attribute_youtube_video: "video id1",
attribute_value_description_id: "1,2,3,4",
attribute_value: [
{
attributeValue: "Bombay Wear",
isSelected: false
},
{
attributeValue: "Terry Cotton",
isSelected: false
},
{
attributeValue: "Nylon",
isSelected: false
},
{
attributeValue: "Sutti",
isSelected: false
}
],
attribute_description: [
{
attributeDescription: "Highest Quality"
},
{
attributeDescription: "Average Choosed"
},
{
attributeDescription: "Silky Cloth"
},
{
attributeDescription: "Old Aged "
}
],
price_per_m: null
},
{
product_type_id: "1",
attribute_id: "2",
attribute_name: "Achara Length",
product_type_method_id: 3,
product_type_type_id: 1,
product_type_unit_id: 3,
attribute_gif_url: "https://i.imgur.com/cr0LVC7.jpg",
attribute_youtube_video: "video id2",
attribute_value_description_id: "5,6,7,8,9,12,13,14",
attribute_value: [
{
attributeValue: "3.5",
isSelected: false
},
{
attributeValue: "4",
isSelected: false
},
{
attributeValue: "4.5",
isSelected: false
},
{
attributeValue: "5",
isSelected: false
},
{
attributeValue: "5.5",
isSelected: false
},
{
attributeValue: "6",
isSelected: false
},
{
attributeValue: "6.5",
isSelected: false
},
{
attributeValue: "7 ",
isSelected: false
}
],
attribute_description: [
{
attributeDescription: "small"
},
{
attributeDescription: "medium"
},
{
attributeDescription: "standard"
},
{
attributeDescription: "standard"
},
{
attributeDescription: "large"
},
{
attributeDescription: "large"
},
{
attributeDescription: "very large"
},
{
attributeDescription: "very large"
}
],
price_per_m: "270"
}
],
[
{
product_type_id: "2",
attribute_id: "4",
attribute_name: "Khadki Cloth",
product_type_method_id: 3,
product_type_type_id: 3,
product_type_unit_id: 3,
attribute_gif_url: "https://i.imgur.com/cr0LVC7.jpg",
attribute_youtube_video: "video id4",
attribute_value_description_id: "10,11",
attribute_value: [
{
attributeValue: "Hand Made",
isSelected: false
},
{
attributeValue: "Ready Made",
isSelected: false
}
],
attribute_description: [
{
attributeDescription: "Mostly Choosed"
},
{
attributeDescription: "Low Cost"
}
],
price_per_m: null
},
{
product_type_id: "2",
attribute_id: "6",
attribute_name: "khadki Color",
product_type_method_id: 3,
product_type_type_id: 3,
product_type_unit_id: 4,
attribute_gif_url: "https://i.imgur.com/cr0LVC7.jpg",
attribute_youtube_video: "video id 6",
attribute_value_description_id: "19,20",
attribute_value: [
{
attributeValue: "white",
isSelected: false
},
{
attributeValue: "mix",
isSelected: false
}
],
attribute_description: [
{
attributeDescription: "poorly choosed"
},
{
attributeDescription: "highly choosed"
}
],
price_per_m: null
}
],
[
{
product_type_id: "3",
attribute_id: "5",
attribute_name: "Blouse Size",
product_type_method_id: 3,
product_type_type_id: 1,
product_type_unit_id: 3,
attribute_gif_url: "https://i.imgur.com/cr0LVC7.jpg",
attribute_youtube_video: "video id",
attribute_value_description_id: "15,16,17,18",
attribute_value: [
{
attributeValue: "15",
isSelected: false
},
{
attributeValue: "20",
isSelected: false
},
{
attributeValue: "25",
isSelected: false
},
{
attributeValue: "30",
isSelected: false
}
],
attribute_description: [
{
attributeDescription: "small"
},
{
attributeDescription: "medium"
},
{
attributeDescription: "standard"
},
{
attributeDescription: "large"
}
],
price_per_m: null
}
]
];
const productTypes = [
{
product_type_name: "Achara Cloth"
},
{
product_type_name: "Khadki "
},
{
product_type_name: "Blouse"
}
];
export default function App() {
return (
<div className="App">
<ProductTypesTable
productTypes={productTypes}
productTypesSizeAttributesList={productTypesSizeAttributesList}
></ProductTypesTable>
</div>
);
function ProductTypesTable({ productTypes, productTypesSizeAttributesList }) {
const [value, setValue] = useState(0);
// product_type_id
return (
<div className="size__table__product__types__attributes__container">
<div className="size__table__product__types__container">
{productTypes.map((obj, index) => {
return (
<ProductTypesTableTabs
index={index}
name={obj.product_type_name}
></ProductTypesTableTabs>
);
})}
</div>
<div className="size__table__attributes">
{/* {ProductSizeAttribtutesDivs[value]} */}
<ProductSizeAttribtutes
productTypesSizeAttributes={productTypesSizeAttributesList[value]}
></ProductSizeAttribtutes>
</div>
<div className="total__price__of__size__attribute">
Total {productTypes[value].product_type_name} Price:
</div>
</div>
);
function ProductTypesTableTabs({ name, index }) {
return (
<div
className={`size__table__product__types inactive_btn__size__table ${
index === value && "active_btn__size__table"
}`}
onClick={() => {
setValue(index);
}}
>
<div className="size__table__product__types__title">{name}</div>
</div>
);
}
}
// product size attribute overall boxes
function ProductSizeAttribtutes({ productTypesSizeAttributes }) {
return (
<div>
{productTypesSizeAttributes.map((obj) => {
return (
<ProductSizeAttributesSingle
obj={obj}
></ProductSizeAttributesSingle>
);
})}
</div>
);
}
function ProductSizeAttributesSingle({ obj }) {
// const [theTabValue, setTheTabValue] = useState(0);
// const [container, setContainer] = useState(obj);
return (
<div>
<div className="select__size__attributes__container">
<div className="select__title__image">
<div className="attribute_name"> SELECT {obj.attribute_name}:</div>
<div className="gif__url__video__conversion">
<div>
<img src={obj.attribute_gif_url} alt="gif url" height="70px" />
</div>
</div>
</div>
<IndividualSelectingAttributeContainer
obj={obj}
// container={container}
// setContainer={setContainer}
></IndividualSelectingAttributeContainer>
</div>
<hr />
</div>
);
}
function IndividualSelectingAttributeContainer({ obj }) {
// const [container, setContainer] = useState(obj.attribute_value);
// const attributeDescriptionContainer = obj && obj.attribute_description;
const [value, setValue] = useState(0);
return (
<div className="selecting_size_containers_div">
{obj.attribute_value.map((objAv, index) => {
return (
<SelectingSizeAttributesContainer
attribute_value={objAv.attributeValue}
// attribute_description={
// // attributeDescriptionContainer &&
// // attributeDescriptionContainer[index]
// // index
// }
container={objAv}
// container={objAv}
// setContainer={setContainer}
value={value}
index={index}
setValue={setValue}
></SelectingSizeAttributesContainer>
);
})}
</div>
);
}
// selecting size attributes container
function SelectingSizeAttributesContainer({
attribute_value,
attribute_description,
container,
setContainer,
value,
index,
setValue
}) {
return (
<div
className={`selecting_size_containers_div_tab ${
index === value && "activated_selected_containers"
}`}
onClick={() => {
setValue(index);
}}
>
<div className="attribute_value">{container.attributeValue}</div>
{/* <div className="attribute_description">{attribute_description}</div> */}
</div>
);
}
}
Need some help in how to make the sub tabs state persistant and also how to store that particular tab value when that particular sub tab is clicked.
Things work fine but the state is not persistant. When i click second tab in the sub tabs, under the second main tab also, the sub tabs state is changed to second tab.
Images
take a look here. basically you have to keep the state of all the selected sub tabs in the main componentץ
to make it more efficient (prevent re-renders of all sub tabs) you should have the state in tab / sub tab hierarchy, and pass only the relevant part of it to the sub tab components
https://codesandbox.io/s/condescending-wing-veye3?file=/src/App.js
I made a simple example of you use case here : https://codesandbox.io/s/stoic-yalow-6v2h9?file=/src/App.js
You will need to keep the state of all the selected item in the sub section. This is a very basic example, you can handle the global state better than the example (maybe with a context or something else).
I'm trying to map through objects to display their values in my React JS project. It looks like I can't access values of the objects within objects using the map function, values simply are not displayed, or if I try to use (.split("/p")) it gives me an error saying "split is not a function".
Link to codesandbox
this is my code:
import React from "react";
import { studies } from "./studies";
import "./styles.css";
const Data = ({ title, text, number, numberdesc }) => {
return (
<>
<div style={{ margin: "1rem" }}>{title}</div>
<div style={{ margin: "1rem" }}>{text}</div>
<div>{number}</div>
<div>{numberdesc}</div>
</>
);
};
function App() {
const appComponent = studies.map((post, i) => {
console.log(studies);
return (
<Data
key={i}
number={studies[i].post.number}
title={studies[i].post.title}
text={studies[i].post.text
.split("/p")
.reduce((total, line) => [total, <br />, <br />, line])}
/>
);
});
return <>{appComponent}</>;
}
export default App;
and {studies} file:
export const studies = [
{
id: "first",
text: "1st Post",
post: {
data: [
{ number: "100", numberdesc: "description1" },
{ number: "200", numberdesc: "description2" },
{ number: "300", numberdesc: "description3" }
],
text: [
{
title: "Title1 from the 1st post",
text: "Text1 from the 1st post."
},
{
title: "Title2 from the 1st post",
text: "Text2 from the 1st post."
},
{
title: "Title3 from the 1st post",
text: "Text3 from the 1st post"
}
]
}
},
{
id: "second",
text: "2nd Post",
post: {
data: [
{ number: "100", numberdesc: "description1" },
{ number: "200", numberdesc: "description2" },
{ number: "300", numberdesc: "description3" }
],
text: [
{
title: "Title1 from the 2nd post",
text: "Text1 from the 2nd post "
},
{
title: "Title2 from the 2nd post",
text: "Text2 /p from the 2nd post"
},
{
title: "Title3 from the 2nd post",
text: "Text3 from the 2nd post"
}
]
}
}
];
What I want to do is to access data and text values for each post, and display them in my Project. Any help and suggestion is greatly appreciated,
Thank you.
I think you may be wanting to .map your array of content. For example:
text={studies[i].post.text.map(t => <p><strong>{t.title}</strong>: {t.text}</p>)}
might replace the existing line that is breaking.
Is this what you're looking for?
function App() {
const appComponent = studies.map(study =>
study.post.data.map((data, k) => (
<Data
key={k}
number={data.number}
numberdesc={data.numberdesc}
title={study.post.text[k].title}
text={study.post.text[k].text}
/>
))
);
return <>{appComponent}</>;
}
Note I arbitrarily zipped data[k]'s number and numberdesc with text[k]'s title and text, but that might not necessarily be what you intend to display.
The above will likely break in case your data and text arrays do not have the same length in any given study.
See it here.
How would i go about rendering a menu with nested <ul> items with an an unknown amount of children in react from an object like in the following example?
[
{
title: "Top level 1",
slug: "top-level-1",
children: [
{
title: "Sub level 1",
slug: "sub-level-1",
children: [
{
title: "Sub Sub Level 1"
slug: "sub-sub-level-1"
}
]
}
{
title: "Sub level 2",
slug: "sub-level-2"
}
]
},
{
title: "Top level 2",
slug: "top-level 2"
}
]
Codesandbox example
You just have to recursively call Menu component for its children to display and pass as a data prop.
let data = [
{
title: "Top level 1",
slug: "top-level-1",
children: [
{
title: "Sub level 1",
slug: "sub-level-1",
children: [
{
title: "Sub Sub Level 1",
slug: "sub-sub-level-1",
children: [
{
title: "Sub Sub Level 2",
slug: "sub-sub-level-2"
}
]
}
]
},
{
title: "Sub level 2",
slug: "sub-level-2"
}
]
},
{
title: "Top level 2",
slug: "top-level 2"
}
];
const Menu = ({data}) => {
return (
<ul>
{data.map(m => {
return (<li>
{m.title}
{m.children && <Menu data={m.children} />}
</li>);
})}
</ul>
);
}
const App = () => (
<div style={styles}>
<Hello name="CodeSandbox" />
<h2>Start editing to see some magic happen {'\u2728'}</h2>
<Menu data={data} />
</div>
);
You could recursively Render the component for nested data which has variable depth.
Sample Snippet.
var data = [
{
title: "Top level 1",
slug: "top-level-1",
children: [
{
title: "Sub level 1",
slug: "sub-level-1",
children: [
{
title: "Sub Sub Level 1",
slug: "sub-sub-level-1"
}
]
},
{
title: "Sub level 2",
slug: "sub-level-2"
}
]
},
{
title: "Top level 2",
slug: "top-level 2"
}
]
const MyComponent = (props) => {
if(Array.isArray(props.collection)) {
return <ul>
{props.collection.map((data)=> {
return <li>
<ul>
<li>{data.title}</li>
<li>{data.slug}</li>
<li><MyComponent collection={data.children}/></li>
</ul>
</li>
})
}
</ul>
}
return null;
}
class App extends React.Component {
render() {
return (
<MyComponent collection={data}/>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
P.S. The snippet contains formatting errors, but I am sure you will be able to rectify that. Snippet was to give an idea of the approach