Adding drag`n`drop(react-dnd) to Material-UI component [TreeView] - javascript

I have a question. I created a TreeView and tried to bind the drag'n'drop, everything works, the TreeItem can be moved. BUT. If you expand any TreeItem and try to drag it, then all its child TreeItems will move with it.
How to make only one TreeItem drag'n'drop, without its child TreeItems????
My guess is I need to access the inner component of the item tree. I also don't know how to do this.
My Code:
export const PathTreeItem = (props: PathTreeItemProps) => {
const [obj, setObj] = useState<TreeItemType[] | undefined>(undefined)
const [isCurrentRequest, setIsCurrentRequest] = useState(false)
const [{ isDragging }, drag] = useDrag({
item: { type: props.obj.description || 'asd' },
canDrag: true,
collect: (monitor: DragSourceMonitor) => ({
isDragging: monitor.isDragging(),
}),
})
const treeItemStyle = useMemo(
() => ({
opacity: isDragging ? 0.4 : 1,
}),
[isDragging]
)
useEffect(() => {
if (isCurrentRequest && props.obj.parameter_type === 'ABC') {
APIs.get(props.obj.name)
.then(res => {
setObj(res.data)
})
.catch(err => {=
console.error('Error ', err)
})
}
}, [isCurrentRequest])
const handleLabelCLick = useCallback(event => {
console.log(event)
setIsCurrentRequest(!isCurrentRequest)
}, [])
return (
<TreeItem
ref={drag}
style={treeItemStyle}
nodeId={props.index}
label={props.obj.description}
onLabelClick={handleLabelCLick}
>
{props.obj.parameter_type === 'ABC' ? (
obj ? (
obj.map((value, index) => (
<PathTreeItem
key={props.keyIndex * 100 + index}
keyIndex={index}
index={`${props.index}.${index}`}
obj={value}
/>
))
) : (
<div></div>
)
) : null}
</TreeItem>
)
}

I have solved that problem by not dragging the TreeItem itself, but a custom component attached to it as its label attribute. Unfortunately, the solution currently only works in Firefox, not in Chrome or Safari:
const CustomItem = () => {
return (
// custom item components (Box, Typography, etc.)
);
}
const DraggableCustomItem = () => {
const [{ isDragging }, drag] = useDrag({
collect: (monitor: DragSourceMonitor) => ({
isDragging: monitor.isDragging()
}),
type: 'CustomItem'
})
return (
<div ref={drag} style={{ opacity: isDragging ? 0.5 : 1}}>
<CustomItem/>
</div>
)
}
const TreeViewDraggableCustomItem = () => {
return (
<TreeView>
<TreeItem key = { '1' }
nodeId = { '1' }
label = { <DraggableCustomItem/> }>
</TreeView>
);
}
See also related SO question, example sandbox and github comment.

Related

Show and Hide Condition in React

I have a simple problem here which I can't figure out. I wanted to hide menus depending on the condition.
For example if status contains at least one "Unlinked". "All unlinked images" menu should appear. I did used .some and I wonder why it doesn't return a boolean.
Codesandbox is here Click here
const showDeleteAllInvalidButton = () => {
const productImages = products?.flatMap((product) =>
product.productImages.filter((image) => image?.status)
);
return productImages?.some((e) => e?.status === "Invalid");
};
const showDeleteAllUnlinkedButton = () => {
const productImages = products?.flatMap((product) =>
product.productImages.filter((image) => image?.status)
);
return productImages?.some((e) => e?.status === "Unlinked");
};
The methods do return a boolean. But in the menus array you are assigning a function reference not the result -
show: showDeleteAllInvalidButton // function reference
show is now assigned a reference to the function showDeleteAllInvalidButton not the result of productImages?.some. You need to invoke the functions when assigning -
show: showDeleteAllInvalidButton() // result of productImages?.some
In your menus object you have a key that contains a function, so if you want this function to filter out your elements you need to execute the show method in side the filter method.
import React, { useState } from "react";
import Button from "#mui/material/Button";
import MenuItem from "#mui/material/MenuItem";
import KeyboardArrowDownIcon from "#mui/icons-material/KeyboardArrowDown";
import CustomMenu from "../../Menu";
const products = [
{
productName: "Apple",
productImages: [
{
status: "Unlinked"
}
]
},
{
productName: "Banana",
productImages: [
{
status: "Unlinked"
}
]
},
{
productName: "Mango",
productImages: [
{
status: "Unlinked"
},
{
status: "Unlinked"
}
]
}
];
const HeaderButtons = () => {
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const showDeleteAllInvalidButton = () => {
const productImages = products?.flatMap((product) =>
product.productImages.filter((image) => image?.status)
);
return productImages?.some((e) => e?.status === "Invalid");
};
const showDeleteAllUnlinkedButton = () => {
const productImages = products?.flatMap((product) =>
product.productImages.filter((image) => image?.status)
);
return productImages?.some((e) => e?.status === "Unlinked");
};
const menus = [
{
id: 1,
name: "Invalid images",
action: () => {
handleClose();
},
show: showDeleteAllInvalidButton
},
{
id: 2,
name: "Unlinked images",
action: () => {
handleClose();
},
show: showDeleteAllUnlinkedButton
},
{
id: 3,
name: "All images",
action: () => {
handleClose();
},
show: () => true // not that I changed it to a function for consistency, but you can check for type in the filter method instead of running afunction
}
];
return (
<div>
<Button
color="error"
aria-haspopup="true"
aria-expanded={open ? "true" : undefined}
variant="outlined"
onClick={handleClick}
endIcon={<KeyboardArrowDownIcon />}
>
Options
</Button>
<CustomMenu anchorEl={anchorEl} open={open} onClose={handleClose}>
{menus
.filter((e) => e.show()) // here is your mistake
.map(
({
id = "",
action = () => {},
icon = null,
name = "",
divider = null
}) => (
<>
<MenuItem key={id} onClick={action} disableRipple>
{icon}
{name}
</MenuItem>
{divider}
</>
)
)}
</CustomMenu>
</div>
);
};
export default HeaderButtons;
In your code, it will always render because your filter functions are evaluating as truth.

The keen-slider__slide class of keen-slider not setting max and min-width properly?

Here is my code:
import React from 'react'
import { useKeenSlider } from 'keen-slider/react'
// Styles
import 'keen-slider/keen-slider.min.css'
interface Props {
children: any
}
// const animation = { duration: 20000, easing: (t: number) => t }
const Slider = ({ children }: Props) => {
// Refs
const [sliderRef] = useKeenSlider<HTMLDivElement>(
{
loop: true,
slides: {
perView: 4
}
},
[
(slider) => {
let timeout: ReturnType<typeof setTimeout>
let mouseOver = false
function clearNextTimeout() {
clearTimeout(timeout)
}
function nextTimeout() {
clearTimeout(timeout)
if (mouseOver) return
timeout = setTimeout(() => {
slider.next()
}, 2000)
}
slider.on('created', () => {
slider.container.addEventListener('mouseover', () => {
mouseOver = true
clearNextTimeout()
})
slider.container.addEventListener('mouseout', () => {
mouseOver = false
nextTimeout()
})
nextTimeout()
})
slider.on('dragStarted', clearNextTimeout)
slider.on('animationEnded', nextTimeout)
slider.on('updated', nextTimeout)
}
]
)
return (
// #ts-ignore
<div ref={sliderRef} className='select-none keen-slider'>
{React.Children.map(children, (child, i) => (
<div
key={`featuredCape-${i}`}
className={`keen-slider__slide sm:h-300 sm:w-300 ${
i !== 0 ? '' : ''
}`}
>
{child}
</div>
))}
</div>
)
}
export default Slider
Here is what I am getting in console
size
In the keen-slider docs there was an option of setting the selection of size to null that this class is doing. When I did that it did made the size correct but but its giving an error
selector: null
selector
Error:
This is the error after adding selector null
The size is now alright!
size after adding selector null
I think that is because when you write {React.Children.map(children, (child, i) => () this does not pass classNames of the child element. So actually classNames that you are setting in child element will not be seen. you should add this logic to the div child.props.className ? ${child.props.className} : ""
<div
key={`featuredCape-${i}`}
className={`keen-slider__slide sm:h-300 sm:w-300
${ i !== 0 ? '' : ''}
child.props.className ? `${child.props.className}` : ""
` }
>
{child}
</div>

TypeError: Cannot read properties of undefined (reading 'url')

I'm working on building a drag and drop card game. I'm not familiar with using the library react-dnd I wonder if this has something to do with it. If I use data already on the file, it works fine, but if I have to fetch the data, it creates This error.
As I said, this usually happens when I use useEffect Somebody has a better idea of how to do this, please let me know.
import React, { useEffect, useState } from 'react';
import Card from './Card';
import { useDrop } from 'react-dnd';
import './Table.css';
const API_Data = [
{
id: 1,
url: 'https://deckofcardsapi.com/static/img/6H.png',
},
{
id: 2,
url: 'https://deckofcardsapi.com/static/img/aceDiamonds.png',
},
{
id: 3,
url: 'https://deckofcardsapi.com/static/img/7C.png',
},
{
id: 4,
url: 'https://deckofcardsapi.com/static/img/6H.png',
},
{
id: 5,
url: 'https://deckofcardsapi.com/static/img/aceDiamonds.png',
},
{
id: 6,
url: 'https://deckofcardsapi.com/static/img/7C.png',
},
];
function Table() {
const [playersCard, setPlayersCard] = useState([]);
const [potA, setPotA] = useState([
{
id: 1,
url: 'https://deckofcardsapi.com/static/img/6H.png',
},
]);
const [potB, setPotB] = useState([]);
/////////////////////////////////////////////////////////////////////////
const [, dropA] = useDrop(() => ({
accept: 'card',
drop: (item) => handleAddToPotA(item.id),
collect: (monitor) => ({
isOver: !!monitor.isOver(),
}),
}));
const handleAddToPotA = (id) => {
const newCard = playersCard.filter((card) => id === card.id);
console.log(`newCard`, newCard);
setPotA((oldCard) => [...oldCard, newCard[0]]);
};
//////////////////////////////////////////////////////////////////////////
const [, dropB] = useDrop(() => ({
accept: 'card',
drop: (item) => handleAddToPotB(item.id),
collect: (monitor) => ({
isOver: !!monitor.isOver(),
}),
}));
const handleAddToPotB = (id) => {
const newCard = playersCard.filter((card) => id === card.id);
setPotB((oldCard) => [...oldCard, newCard[0]]);
console.log(newCard);
};
useEffect(() => {
setPlayersCard(API_Data);
return () => {};
}, []);
//////////////////////////////////////////////////////////////////////////
if (!playersCard) {
return <div></div>;
}
return (
<div className="table-page">
<div className="center-table">
<div className="pot-a" ref={dropA}>
{potA &&
potA.map((card) => {
return <Card url={card?.url} id={card.id} key={card.id} />;
})}
</div>
<div className="pot-b" ref={dropB}>
{potB &&
potB.map((card) => {
return <Card url={card.url} id={card.id} key={card.id} />;
})}
</div>
</div>
<div className="players-card">
{playersCard.map((card) => {
return <Card url={card.url} id={card.id} key={card.id} />;
})}
</div>
</div>
);
}
export default Table;
Because playersCard is not initialized yet.
try to check undefined first
<div className="players-card">
{playersCard.length > 0 && playersCard.map((card) => {
return <Card url={card.url} id={card.id} key={card.id} />;
})}
</div>
A Possible Reason:
Check your function and method names for typos

Storybook fetch http addons knobs

The result I would like to obtain in the first image, an input field where a url is put from which the data must be taken.
In the second image you see the problem I am getting when I try to get the data from http.
The module works if I don't use it within the storybook.
So I don't understand where the problem is.
Can anyone help me out?
import React, { useState, useEffect } from 'react';
import { storiesOf } from '#storybook/react-native';
import { withKnobs, object, text } from '#storybook/addon-knobs';
import { Chip, Text } from 'react-native-paper';
import Datatable from './DatatableFetch';
const columns = [
{
name: 'Name',
selector: 'name',
sortable: true,
},
{
name: 'Trips',
selector: 'trips',
sortable: true,
cell: ({ trips }) => <Chip mode="outlined">{trips}</Chip>,
right: true,
},
];
const App = ({url}) => {
const [state, setState] = useState({
data3: [],
page: 1,
perPage: 5,
totalRows: 1,
});
const { data3, page, totalRows, perPage } = state;
const child = (obj, str) => {
const props = str.split('.');
let child = obj;
props.forEach((prop) => (child = child[prop]));
return child;
};
const load = (sort, sortField = '', sortAsc = false) =>
fetch(
url +
(page - 1) +
'&size=' +
perPage
)
.then((response) => response.json())
.then(({ data, totalPassengers }) => {
var copyData = Object.assign([], data);
var result = sort
? copyData
: copyData.sort((a, b) => {
let [x, z] = sortAsc ? [a, b] : [b, a];
return child(x, sortField) > child(z, sortField) ? 1 : -1;
});
setState((prev) => ({
...prev,
data3: result,
totalRows: totalPassengers,
}));
})
.catch((error) => {
console.error(error);
});
useEffect(() => {
load(false);
}, [page]);
return (
<Datatable
columns={object('columns', columns, 'Columns')}
data={object('data', data3, 'Data')}
//data={data3}
striped
stripedColors={['#2196f3', '#ccc']}
viewLoader
defaultSortField={'name'}
defaultSortAsc={true}
paginationServer
paginationTotalRows={totalRows}
onChangePage={(_inPage) =>
setState((prev) => ({
...prev,
page: _inPage,
}))
}
onSort={(selector, sortDirection) =>
load(true, selector, sortDirection === 'asc')
}
paginationComponentOptions={(currentPage, totalPage) =>
`${currentPage} di ${totalPage}`
}
style={{
backgroundColor: "#ccc",
borderRadius: 4,
}}
/>
);
};
storiesOf('DatatableFetch', module)
.addDecorator(withKnobs)
.add('example', () => {
return <App url={text("Url", "https://api.instantwebtools.net/v1/passenger?page=", "Url")} />;
});
DatatableFetch.js
import React, { useState, useEffect, useCallback } from 'react';
import { View, Animated } from 'react-native';
import { DataTable, ProgressBar, Colors, Text } from 'react-native-paper';
function Datatable({
columns,
data = [],
noTableHead = false,
striped = false,
stripedColors = ['#2196f3','#8bc34a'],
viewLoader = false,
defaultSortField,
defaultSortAsc,
page: _page = 1,
perPage: _perPage = 5,
pagination = false,
paginationComponentOptions,
noDataComponent = <Text>There are no records to display.</Text>,
style,
paginationServer = false,
paginationTotalRows = 0,
onChangePage,
onSort
}) {
const [state, setState] = useState({
datatable: data,
page: _page - 1,
perPage: _perPage,
numberOfPages: Math.ceil( (paginationServer ? paginationTotalRows : data.length) / _perPage),
sortField: defaultSortField,
sortAsc: defaultSortAsc,
progressBar: false,
loading: false,
});
const {
datatable,
page,
perPage,
numberOfPages,
sortField,
sortAsc,
progressBar,
loading,
} = state;
/*useEffect(() => {
setState((prev) => ({
...prev,
datatable: data,
numberOfPages: Math.ceil( (paginationServer ? paginationTotalRows : data.length) / _perPage)
}));
}, [data]);*/
const dataPagination = !paginationServer && pagination
? datatable.slice(perPage * page, perPage * (page + 1))
: datatable;
const getValue = (object, { selector, cell }) => {
if (cell) return cell(object);
return selector
.replace(/\[/g, '.')
.replace(/\]/g, '')
.split('.')
.reduce((o, k) => (o || {})[k], object);
};
useEffect(() => {
console.log('a')
setState((prev) => ({
...prev,
progressBar: true,
}));
sort(sortField, sortAsc);
}, [data]);
const child = (obj, str) => {
const props = str.split('.');
let child = obj;
props.forEach((prop) => (child = child[prop]));
return child;
};
const sort = async (sortField, sortAsc) => {
var copyData = Object.assign([], data);
var result = paginationServer ? copyData : copyData.sort((a, b) => {
let [x, z] = sortAsc ? [a, b] : [b, a];
return child(x, sortField) > child(z, sortField) ? 1 : -1;
});
setTimeout(() => {
setState((prev) => ({
...prev,
datatable: result,
numberOfPages: Math.ceil( (paginationServer ? paginationTotalRows : result.length) / _perPage),
progressBar: false,
loading: true,
}));
}, 2000);
};
return (
<DataTable style={style}>
{!noTableHead && (
<DataTable.Header>
{columns.map((item, i) => {
const sortDirection =
datatable.length > 0 &&
!progressBar &&
sortField === item.selector
? !sortAsc
? 'ascending'
: 'descending'
: null;
return (
!item.omit && (
<DataTable.Title
key={i}
onPress={() => {
if (item.sortable) {
const sortAscC =
sortField === item.selector ? !sortAsc : true;
setState((prev) => ({
...prev,
sortField: item.selector,
sortAsc: sortAscC,
progressBar: true,
}));
if(paginationServer) onSort(item.selector, sortAscC ? 'asc' : 'desc')
else sort(item.selector, sortAscC);
}
}}
sortDirection={sortDirection}
numeric={item.right}
style={item.styleHeader}>
{item.name} {false && sortDirection}
</DataTable.Title>
)
);
})}
</DataTable.Header>
)}
{viewLoader && progressBar && (
<ProgressBar indeterminate color={Colors.blue900} />
)}
{!progressBar && datatable.length === 0 && (
<View
style={{
margin: 10,
justifyContent: 'center',
alignItems: 'center',
}}>
{noDataComponent}
</View>
)}
{loading &&
dataPagination.map((item, i) => {
return (
<DataTable.Row
key={i}
style={{
opacity: progressBar ? 0.2 : 1,
backgroundColor: striped
? i % 2
? stripedColors[0]
: stripedColors[1]
: null,
}}>
{columns.map((headerItem, j) => {
return (
!headerItem.omit && (
<DataTable.Cell
key={j}
numeric={headerItem.right}
style={headerItem.styleRow}>
{getValue(item, headerItem)}
</DataTable.Cell>
)
);
})}
</DataTable.Row>
);
})}
{(pagination || paginationServer) && (
<DataTable.Pagination
page={page}
numberOfPages={numberOfPages}
onPageChange={(page) => {
onChangePage && onChangePage(page+1)
if(true){
setState((prev) => ({
...prev,
progressBar: true,
}));
setTimeout(() => {
setState((prev) => ({
...prev,
page: page,
progressBar: false,
}));
}, 2000);
}
}}
label={paginationComponentOptions ? paginationComponentOptions(page + 1, numberOfPages) : `${page + 1}-${numberOfPages} of ${datatable.length}`}
/>
)}
</DataTable>
);
}
export default Datatable;

How to preserve the order of an array of objects manipulated from my state upon the execution of functions

I am facing an Issue on the navigation function of a form I am building.
The below and linked code is the dummiest version possible to try an shorten and simplify the issue.
I will post my whole code below and you can run it here as well.
It's a form that the user fills and navigates from step to step until the final submission of the data.
The navigation buttons work well, using this.inputMenuNavigationHandler(App.js Line: 25), going from section to section, but on a specific section I have created kind of sub-sections, which are alternated using this.subOptionNavigation function.
When I alternate the info inside case "foo2"(switch function of Input.js - line: 15) and afterwards call this.inputMenuNavigationHandler again, for some reason the sections get out of order, and instead of navigating from secondMenu to thirdMenu, it takes me to firstMenu.
I do not understand why this is happening, as the navigation function, which uses the classArray.indexOf(classElement), in other words, the order of the array of objects, does not interfere or relates to what the value of this.state.newClassForm.secondMenu.internalNavigation is...
App.js File
import React from "react";
import Input from "./Input";
import "./styles.css";
export default class App extends React.Component {
state = {
classNavigation: "firstMenu",
newClassForm: {
firstMenu: {
elementType: "foo1",
title: "First Section"
},
secondMenu: {
elementType: "foo2",
title: "Second Section",
internalNavigation: "tba"
},
thirdMenu: {
elementType: "foo3",
title: "Third Section"
}
}
};
inputMenuNavigationHandler = (classArray, classElement, command) => {
let index = classArray.indexOf(classElement);
if (command === "increment" && index < classArray.length - 1) {
let incrementModal = classArray[index + 1].id;
this.setState(({ classNavigation, ...restTop }) => ({
classNavigation: incrementModal,
...restTop
}));
}
if (command === "decrement" && index > 0) {
let decrementModal = classArray[index - 1].id;
this.setState(({ classNavigation, ...restTop }) => ({
classNavigation: decrementModal,
...restTop
}));
}
};
subOptionNavigation = childSubOption => {
this.setState(
({
newClassForm: {
secondMenu: { internalNavigation, ...restSecondMenu },
...restNewClassForm
},
...restTop
}) => ({
newClassForm: {
secondMenu: { internalNavigation: childSubOption, ...restSecondMenu },
...restNewClassForm
},
...restTop
})
);
};
render() {
let classArray = [];
for (let key in { ...this.state.newClassForm }) {
classArray.push({
id: key,
config: this.state.newClassForm[key]
});
}
return (
<div className="App">
{classArray.map(cl =>
cl.id.indexOf(this.state.classNavigation) !== -1 ? (
<div
key={cl.id}
style={{
padding: "10px",
border: "1px solid black",
margin: "5px"
}}
>
<Input
classArray={classArray}
classElement={cl}
elementType={cl.config.elementType}
title={cl.config.title}
navigationIncrement={() =>
this.inputMenuNavigationHandler(classArray, cl, "increment")
}
navigationDecrement={() =>
this.inputMenuNavigationHandler(classArray, cl, "decrement")
}
internalNavigationDisplay={
this.state.newClassForm.secondMenu.internalNavigation
}
toOption1={() => this.subOptionNavigation("subOption1")}
toOption2={() => this.subOptionNavigation("subOption2")}
/>
</div>
) : null
)}
</div>
);
}
}
Input.js
import React from "react";
export default function Input(props) {
let input = null;
switch (props.elementType) {
case "foo1":
input = (
<div>
<div>{props.title}</div>
<button onClick={props.navigationDecrement}>Previous</button>
<button onClick={props.navigationIncrement}>Next</button>
</div>
);
break;
case "foo2":
input = (
<div>
{props.internalNavigationDisplay === "tba" ? (
<div>
<div>{props.title}</div>
<button onClick={props.toOption1}>Option 1</button>
<button onClick={props.toOption2}>Option 2</button>
</div>
) : null}
{props.internalNavigationDisplay === "subOption1" ? (
<div>
<div>SubOption 1</div>
<button onClick={props.navigationDecrement}>Previous</button>
<button onClick={props.navigationIncrement}>Next</button>
</div>
) : null}
{props.internalNavigationDisplay === "subOption2" ? (
<div>
<div>SubOption 2</div>
<button onClick={props.navigationDecrement}>Previous</button>
<button onClick={props.navigationIncrement}>Next</button>
</div>
) : null}
</div>
);
break;
case "foo3":
input = (
<div>
<div>{props.title}</div>
<button onClick={props.navigationDecrement}>Previous</button>
<button onClick={props.navigationIncrement}>Next</button>
</div>
);
break;
default:
input = null;
}
return { ...input };
}
Well!!!
Here is my solution in order to preserve the index
I have created a button, which will start the navigation:
<div className="App">
<h1>Create a new class?</h1>
<button onClick={this.newClassHandler}>Go!!</button>
{this.state.createNewClass
? this.state.newClassArray.map //...map the stuff and render the input sections
So on the newClassHandler function I move the code that once was in my render part of the component, which was the reason why the index was getting screwed up, as some bug may happen when I navigate into the sub section of the second input, once the component re renders.
Does any one have a solution which does not require the use of state???
import React from "react";
import Input from "./Input";
import "./styles.css";
export default class App extends React.Component {
state = {
indexOfNavigation: 0,
createNewClass: false,
newClassArray: [],
classNavigation: "firstMenu",
newClassForm: {
firstMenu: {
elementType: "foo1",
title: "First Section"
},
secondMenu: {
elementType: "foo2",
title: "Second Section",
internalNavigation: "tba"
},
thirdMenu: {
elementType: "foo3",
title: "Third Section"
}
}
};
newClassHandler = () => {
let classArray = [];
for (let key in { ...this.state.newClassForm }) {
classArray.push({
id: key,
config: this.state.newClassForm[key]
});
}
this.setState(({ createNewClass, newClassArray, ...restTop }) => ({
createNewClass: true,
newClassArray: classArray,
...restTop
}));
};
inputMenuNavigationHandler = (classArray, classElement, command) => {
let index = classArray.indexOf(classElement);
console.log("index before setting my state", index);
console.log("element Id before navigating - ", classElement.id);
this.setState(
({ indexOfNavigation, ...restTop }) => ({
indexOfNavigation: index,
...restTop
}),
() => {
if (
command === "increment" &&
this.state.indexOfNavigation < classArray.length - 1
) {
this.setState(
({ indexOfNavigation, ...restTop }) => ({
indexOfNavigation: indexOfNavigation + 1,
...restTop
}),
() => {
console.log(
"index after setting state",
this.state.indexOfNavigation
);
let incrementModal = classArray[this.state.indexOfNavigation].id;
this.setState(
({ classNavigation, ...restTop }) => ({
classNavigation: incrementModal,
...restTop
}),
() => {
console.log(
"element Id being received after navigating",
classElement.id
);
console.log(
"classNavigation info after navigating",
this.state.classNavigation
);
}
);
}
);
}
if (command === "decrement" && this.state.indexOfNavigation > 0) {
this.setState(
({ indexOfNavigation, ...restTop }) => ({
indexOfNavigation: indexOfNavigation - 1,
...restTop
}),
() => {
console.log(
"index after setting state",
this.state.indexOfNavigation
);
let incrementModal = classArray[this.state.indexOfNavigation].id;
this.setState(
({ classNavigation, ...restTop }) => ({
classNavigation: incrementModal,
...restTop
}),
() => {
console.log(
"element Id being received after navigating",
classElement.id
);
console.log(
"classNavigation info after navigating",
this.state.classNavigation
);
}
);
}
);
}
}
);
};
subOptionNavigation = childSubOption => {
this.setState(
({
newClassForm: {
secondMenu: { internalNavigation, ...restSecondMenu },
...restNewClassForm
},
...restTop
}) => ({
newClassForm: {
secondMenu: { internalNavigation: childSubOption, ...restSecondMenu },
...restNewClassForm
},
...restTop
}),
() => {
console.log(
"index of main array once navigating in the sub menu",
this.state.indexOfNavigation
);
console.log(
"classNavigation info after navigating in sub menus",
this.state.classNavigation
);
}
);
};
render() {
//here I change the format of the new class form object
let classArray = [];
for (let key in { ...this.state.newClassForm }) {
classArray.push({
id: key,
config: this.state.newClassForm[key]
});
}
return (
<div className="App">
<h1>Create a new class?</h1>
<button onClick={this.newClassHandler}>Go!!</button>
{this.state.createNewClass
? this.state.newClassArray.map(cl =>
cl.id.indexOf(this.state.classNavigation) !== -1 ? (
<div
key={cl.id}
style={{
padding: "10px",
border: "1px solid black",
margin: "5px"
}}
>
<Input
classArray={this.state.newClassArray}
classElement={cl}
elementType={cl.config.elementType}
title={cl.config.title}
navigationIncrement={() =>
this.inputMenuNavigationHandler(
this.state.newClassArray,
cl,
"increment"
)
}
navigationDecrement={() =>
this.inputMenuNavigationHandler(
this.state.newClassArray,
cl,
"decrement"
)
}
internalNavigationDisplay={
this.state.newClassForm.secondMenu.internalNavigation
}
toOption1={() => this.subOptionNavigation("subOption1")}
toOption2={() => this.subOptionNavigation("subOption2")}
/>
</div>
) : null
)
: null}
</div>
);
}
}
I could also preserve my array of manipulated objects in order to keep the array on the same order just by using a lifecycle hook:
moving the code from the render part, and storing in my state, and then rendering this state on input in order to run my logic, it works as well.
componentDidMount() {
console.log("component did mount");
let classArray = [];
for (let key in { ...this.state.newClassForm }) {
classArray.push({
id: key,
config: this.state.newClassForm[key]
});
}
this.setState(
({ newClassArray, ...restTop }) => ({
newClassArray: classArray,
...restTop
}),
() => console.log(this.state.newClassArray)
);
}

Categories