Recursive function in Reactjs - javascript

I am making dynamic menus using a recursive function and I have already made the menus and it display in the right order without any issues.
And I receive the data for menu from service.js file and you can see the entire working application in the below code sandbox example,
https://codesandbox.io/s/reactstrap-accordion-3uoz9
Requirement:
Here I am in the need to find out the last level of menus and need to assign checkbox with value as their respective id {item.id}.
Eg:
For First menu one,
-> [Checkbox with value as 1.1.1] One-One-One
-> [Checkbox with value as 1.1.2] One - one - two
-> [Checkbox with value as 1.1.3] One - one - three
For Second menu two,
-> [Checkbox with value as 2.1] Two - one
.
.
.
For sixth menu six,
-> [Checkbox with value as 6] Six
I hope the point is clear that I need to find out the last level in recursion and should assign a checkbox to it with the value of their id.
Please fork the code sandbox provided and help me to achieve the result of making the checkbox at the last level.
Optional requirement:
The collapse is working for whole menus at once if possible please make it collapse at each individual level in unique.
But the main important requirement is to make a checkbox at the last level of menus.
A big thanks in advance...
Edit:
As commented by Crowder, I have created the snippet removing reactstrap code and it is okay now because I am in the need of displaying checkbox inline to laste level of submenus (last children elements).
const menuData = [
{
id: "1",
name: "One",
children: [
{
id: "1.1",
name: "One - one",
children: [
{ id: "1.1.1", name: "One - one - one" },
{ id: "1.1.2", name: "One - one - two" },
{ id: "1.1.3", name: "One - one - three" }
]
}
]
},
{
id: "2",
name: "Two",
children: [{ id: "2.1", name: "Two - one" }]
},
{
id: "3",
name: "Three",
children: [
{
id: "3.1",
name: "Three - one",
children: [
{
id: "3.1.1",
name: "Three - one - one",
children: [
{
id: "3.1.1.1",
name: "Three - one - one - one",
children: [
{ id: "3.1.1.1.1", name: "Three - one - one - one - one" }
]
}
]
}
]
}
]
},
{ id: "4", name: "Four" },
{
id: "5",
name: "Five",
children: [
{ id: "5.1", name: "Five - one" },
{ id: "5.2", name: "Five - two" },
{ id: "5.3", name: "Five - three" },
{ id: "5.4", name: "Five - four" }
]
},
{ id: "6", name: "Six" }
];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
currentSelection: "",
menuItems: [],
isToggleOpen: false
};
}
componentDidMount() {
this.setState({ menuItems: menuData });
}
handleClick(id, evt) {
evt.preventDefault();
console.log("click handler called with", id);
this.setState({ currentSelection: id });
}
toggle() {
console.log(this.state);
this.setState({
isToggleOpen: !this.state.isToggleOpen
});
}
buildMenu(items) {
return (
<ul>
{items &&
items.map(item => (
<li key={item.id}>
<div>
{item.name}
{item.children && item.children.length > 0
? this.buildMenu(item.children)
: null}
</div>
</li>
))}
</ul>
);
}
render() {
return (
<div>
<h2>Click any of the below option</h2>
{this.state.menuItems &&
this.state.menuItems.map((item, index) => {
return (
<div key={index}>
{item.name}
{this.buildMenu(item.children)}
</div>
);
})}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/reactstrap/4.8.0/reactstrap.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/reactstrap/4.8.0/reactstrap.min.css" />
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.development.js"></script>

buildMenu(items) {
return (
<ul>
{items &&
items.map((item, index) => (
<li key={item.id}>
<div>
{this.state.isToggleOpen}
{(item.children) ? 'Not Last': 'Here you can apply your check box'} //this check if item have children
<Button onClick={this.toggle.bind(this)}> {item.name} {index} </Button>
<Collapse isOpen={this.state.isToggleOpen}>
{item.children && item.children.length > 0
? this.buildMenu(item.children, index)
: null}
</Collapse>
</div>
</li>
))}
</ul>
);
}
Now second case on render
<Button onClick={this.toggle.bind(this)}> {item.name}</Button>
Check if item have children
{(item.children) ? this.buildMenu(item.children) : 'Apply your checkbox here'}
Full Code
import React from "react";
import { render } from "react-dom";
import { loadMenu } from "./service";
import { Button, Collapse } from "reactstrap";
// const buildMenu = function buildMenu(items)
// const Menu = ({ items }) => buildMenu(items);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
currentSelection: "",
menuItems: [],
isToggleOpen: false
};
}
componentDidMount() {
loadMenu().then(items => this.setState({ menuItems: items }));
}
handleClick(id, evt) {
evt.preventDefault();
console.log("click handler called with", id);
this.setState({ currentSelection: id });
}
toggle() {
console.log(this.state);
this.setState({
isToggleOpen: !this.state.isToggleOpen
});
}
buildMenu(items) {
return (
<ul>
{items &&
items.map(item => (
<li key={item.id}>
<div>
{this.state.isToggleOpen}
{(item.children) ? 'Not Last': 'Here you can apply your check box'}
<Button onClick={this.toggle.bind(this)}> {item.name} </Button>
<Collapse isOpen={this.state.isToggleOpen}>
{item.children && item.children.length > 0
? this.buildMenu(item.children)
: null}
</Collapse>
</div>
</li>
))}
</ul>
);
}
render() {
console.log(this.state.menuItems);
return (
<div>
<h2>Click any of the below option</h2>
{this.state.menuItems &&
this.state.menuItems.map((item, index) => {
return (
<div>
<Button onClick={this.toggle.bind(this)}> {item.name} </Button>
{(item.children) ? 'Not Last': 'Here you can apply your check box'}
<Collapse isOpen={this.state.isToggleOpen}>
{this.buildMenu(item.children)}
</Collapse>
</div>
);
})}
</div>
);
}
}
render(<App />, document.getElementById("root"));

Related

Data object with array of different groups, trying to display ordered by group type using .map() and pushing to new array nothing is rendering

I have a React Component I'm building out that accepts a data object as props.
This data object holds an array of groups each with it's own group type.
What I'm trying to do is map over each group in the array and display it's contents as it's own section, but I also want to ensure that all of the objects with the type grid display together, followed by any sections with a list type no matter what position they are in the array.
So even if the array holds the values like:
[{Grid}, {List}, {Grid}, {List}, {Grid}]
It will display like this:
Group #1 (Grid)
Contents
Group #2 (Grid)
Contents
Group #3 (Grid)
Contents
Group #4 (List)
Contents
Group #5 (List)
Contents
The problem I'm having is that I wrote two separate components to handle the styling of each type and then passed them into a function that creates a new array from the original to then render, and nothing is displaying. I'm not getting any error messages or anything in the console so I'm completely stumped as to where I'm going wrong.
Here are my components, the data structure and a Codesandbox:
// Component File
import "./styles.css";
import data from "./resourceMock";
import FileIcon from "./FileIcon";
const GridView = (group) => {
const { groupName, items } = group;
return (
<>
<h2>{groupName}</h2>
<ul
style={{
display: "inline-flex",
flexWrap: "wrap",
listStyleType: "none"
}}
>
{items.map((item) => {
return (
<li style={{ height: "40vh", flexGrow: 1 }}>
<img src={item.img} style={{ height: "150px", width: "150px" }} />
<h4>{item.name}</h4>
<h5>{item.subtitle}</h5>
</li>
);
})}
</ul>
</>
);
};
const ListView = (group) => {
const { groupName, items } = group;
return (
<>
<h2>{groupName}</h2>
<ul style={{ listStyleType: "none" }}>
{items.map((item) => {
return (
<li>
<FileIcon />
{item.title}
</li>
);
})}
</ul>
</>
);
};
function renderList(group) {
const lists = [];
if (!group) return null;
data.map((group) => {
switch (group.groupType) {
case "grid":
return lists.push((group) => {
<GridView group={group} />;
});
case "list":
return lists.push((group) => {
<ListView group={group} />;
});
default:
return lists.push((group) => {
<ListView group={group} />;
});
}
});
return lists;
}
export default function App() {
return <div className="App">{data.map((group) => renderList(group))}</div>;
}
Data Structure:
export default [
{
groupName: "Marvel Characters",
groupType: "grid",
items: [
{
name: "Iron Man",
subtitle: "Inventor Tony Stark",
img:
"https://www.denofgeek.com/wp-content/uploads/2019/02/mcu-1-iron-man.jpg?resize=768%2C432"
},
{
name: "Incredible Hulk",
subtitle: "Bruce Banner",
img:
"https://lh3.googleusercontent.com/proxy/-jHnFcGLqlxjdOl9Mf99UPBk4XJKcQ1Hsv7lPYEs8Vai874sW0l5TUwn3acriwGpE36aUDPpZHPFzccRUt7b7POGOWCFIbgYomTO9bDCXF0eovxFGdr_D3P-0wfLnkUMOOJDG09MgAzqSCbiDq-A"
}
]
},
{
groupName: "Magic Cards",
groupType: "list",
items: [
{
title: "Kamahl, Fist Of Krosa",
link:
"https://gatherer.wizards.com/pages/card/Details.aspx?multiverseid=220490"
},
{
title: "Seedborn Muse",
link:
"https://gatherer.wizards.com/pages/card/Details.aspx?multiverseid=446180"
}
]
},
{
groupName: "DC Characters",
groupType: "grid",
items: [
{
name: "Batman",
subtitle: "Bruce Wayne",
img:
"https://static.wikia.nocookie.net/marvel_dc/images/a/a6/Batman_Vol_2_2_Variant_Textless.jpg/revision/latest/top-crop/width/360/height/450?cb=20120228075313"
},
{
name: "Martian Manhunter",
subtitle: "J'onn J'onzz",
img:
"https://cdn.flickeringmyth.com/wp-content/uploads/2021/03/Martian-Manhunter-600x338.png"
}
]
},
{
groupName: "Kaiju and Mechs",
groupType: "grid",
items: [
{
name: "Godzilla",
img:
"https://www.denofgeek.com/wp-content/uploads/2019/05/godzillakingofmonsters-2.jpg?resize=768%2C432"
},
{
name: "Hunter Vertigo",
img: "https://i.ytimg.com/vi/7F-iZYAqSbw/maxresdefault.jpg"
}
]
},
{
groupName: "Comic Books",
groupType: "list",
items: [
{
title: "Descender",
link: "https://imagecomics.com/comics/series/descender"
},
{
title: "East of West",
link: "https://imagecomics.com/comics/series/east-of-west"
},
{
title: "Letter 44",
link: "https://onipress.com/collections/letter-44"
}
]
}
];
I have fixed the issues.
Sandbox: https://codesandbox.io/s/affectionate-sinoussi-5suro
You are already looping over data inside renderList, so we can directly have <div className="App">{renderList(data)}</div>;
To sort, we can use Array.sort()
data.sort((a, b) => a.groupType === b.groupType ? 0 : a.groupType > b.groupType ? 1 : -1);
Also, in switch case you need to push the component and not a function.
case "grid":
lists.push(<GridView group={group} />);
break;
Need to use destructuring here const ListView = ({ group }) => {}
Finally add key to your lists. I have added using the item name, but you need to change per your requirement.

How to prev and next button click navigate the nested array into the react

One of my requirement into the react is to put the button inside the array.map on each nested links and also contains the button prev and next.
Here is the Format of the my array collections::
const myTest = [
{
links: [
{
id: "1",
name: "One",
links: [
{
id: "1.1",
name: "11"
},
{
id: "1.2",
name: "12"
},
{
id: "1.3",
name: "13"
}
]
},
{
id: "2",
name: "Two",
links: [
{
id: "2.1",
name: "21"
},
{
id: "2.2",
name: "22"
},
{
id: "2.3",
name: "23"
}
]
}
]
}
];
Here I tried using the react code and code as following ::
let menuItems = myTest[0].links;
const { itemCount } = this.state;
return (
<div>
<div className="listContainer">
{menuItems.map((item, index) => (
<div>
<Row className="rowContainer">
<a
style={{ paddingRight: "10px" }}
href="#"
onClick={this.handlePrevious}
>
«
</a>
{item.links.map(
(subItem, subIndex) =>
subIndex < itemCount && (
<div style={{ paddingRight: "20px" }}>{subItem.name}</div>
)
)}
<a href="#" onClick={this.handleNext}>
»
</a>
</Row>
</div>
))}
</div>
</div>
);
Once user click on next it will move to next available items in array and same as previous.
I have tried and initially I am able to display the itemCount only 2 from the array but once I am click on next and previous how can we navigate inside map that I am confused .
Could someone take a look into the following URL all the code which I have been tried in this codesandbox URL
Codesandbox URL which I tried
Let me know if any other further information required?

Attempting to show array element of react select in list item

I am using react-select to store multiple elements and am using the map function to display elements which is working fine. But when I am using the same element in another class to display in a list element it shows a blank.
Here is the code where I am displaying the multiple options.
const Departments = [
{ label: "OneIT", value: "OneIT" },
{ label: "HR", value: "HR" },
{ label: "Vigilance", value: "Vigilance" },
{ label: "Ethics", value: "Ethics" },
{ label: "Corporate Services", value: "Corporate Services" },
{ label: "Legal", value: "Legal" },
{ label: "Sports", value: "Sports" },
{ label: "TQM", value: "TQM" },
{ label: "Iron Making", value: "Iron Making" },
{ label: "TMH", value: "TMH" }
];
class MultiSelect2 extends Component {
state = {
selectedOptions: []
};
handleChangeField = selectedOptions => {
this.setState({ selectedOptions });
};
render() {
const { selectedOption } = this.state;
return (
<div className="container">
<div className="row">
<div className="col-md-2"></div>
<div className="col-md-8">
<span>Select Department</span>
<Select
value={selectedOption}
options={Departments}
onChange={this.handleChangeField}
isMulti
/>
{this.state.selectedOptions.map(o => (
<p>{o.value}</p>
))}
</div>
<div className="col-md-4"></div>
</div>
</div>
);
}
}
I am trying to display this in another class in the list item but it is not showing.
export class Confirm extends Component {
state = {
selectedOptions: []
};
render() {
const {
values: {selectedOptions
}
} = this.props;
return (
<List>
<ListItemText primary="Departments" secondary={selectedOptions} />
</List>

Not All Default Parameters Are Rendering

Can anyone tell me why one default parameter renders the default value while the other doesn't?
Take this Nav component and its' props being passed. The heading prop renders however the props inside of navItems array does not. It will console log as undefined.
Note: If I remove/comment out all the props inside navItems, the default values do render.
Component:
const Nav = ({
heading = "This Default Value Renders!",
navItems = [
{
id: 0,
label: "This Value Does Not Render",
subMenu: [
{
id: 1,
item: "This doesn't either",
},
],
},
],
}) => {
return (
<h1>{heading}</h1>
{navItems.map(item => (
<li>
{item.label}
<ul>
{item.subMenu.map(subItem => (
<li>{subItem.item}</li>
))}
</ul>
</li>
))}
)
}
Component used in parent component with props being passed.
<Nav
// heading = "I am the heading",
navItems = {[
{
id: 1,
// label: "About",
subMenu: [
{
id: 1,
// item: "About Sub Item 1",
},
{
id: 2,
// item: "About Sub Item 2",
},
],
},
]}
/>

items are not draggable

I am using library called react-beautiful-dnd for draggable lists. The use case of my application is there will be a nested list which should be draggable and the items inside its corresponding list should also be draggable. The items from List1 should not be dragged to List2. For this I am trying to create draggable vertical list which is not working. The items are not draggable at all. I have created a sandbox of this either.
here it is
https://codesandbox.io/s/vqzx332zj7
The source code is
import DraggableSubItems from "./DraggableSubItems";
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
const items = [
{
id: 1,
name: "Top Level",
path: "top-level",
children: [
{
id: 1,
name: "dropbox",
path: "dropbox"
},
{
id: 2,
name: "asana",
path: "asana"
}
]
},
{
id: 2,
name: "Frontend Library",
path: "frontend-library",
children: [
{
id: 1,
name: "reactjs",
path: "reactjs"
},
{
id: 2,
name: "vuejs",
path: "vuejs"
}
]
}
];
const grid = 8;
const getListStyle = () => ({
padding: grid,
display: "flex",
flexDirection: "column"
});
class DraggableItems extends React.Component {
constructor(props) {
super(props);
this.state = {
items
};
}
onDragEnd = result => {
console.log("drag", result);
// dropped outside the list
if (!result.destination) {
return null;
}
if (result.destination.index === result.source.index) {
return;
}
const items = reorder(
this.state.items,
result.source.index,
result.destination.index
);
console.log("items", items);
this.setState({
items
});
};
render() {
return (
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId="droppable">
{(provided, snapshot) => {
console.log("provided", provided, snapshot);
return (
<div
ref={provided.innerRef}
style={getListStyle(snapshot.isDraggingOver)}
>
{this.state.items.map(item => (
<Draggable
key={item.id}
draggableId={item.id}
index={item.id}
>
{(draggableProvided, draggableSnapshot) => {
console.log(
"provided inside Draggable",
draggableProvided,
draggableSnapshot
);
return (
<React.Fragment>
<div
ref={draggableProvided.innerRef}
{...draggableProvided.draggableProps}
style={getListStyle(
draggableSnapshot.isDragging,
draggableProvided.draggableProps.style
)}
{...draggableProvided.dragHandleProps}
>
<h3>{item.name}</h3>
<DraggableSubItems
subitems={item.children ? item.children : []}
type={item.id}
/>
</div>
{draggableProvided.placeholder}
</React.Fragment>
);
}}
</Draggable>
))}
</div>
);
}}
</Droppable>
</DragDropContext>
);
}
}
export default DraggableItems;

Categories

Resources