In the parent component manage-categories.jsx I have an array declared categoryTypes
const categoryTypes = [
{ name: "category", values: props.categories, active: true },
{ name: "type", values: props.types },
{ name: "finish", values: props.finishes },
{ name: "profile", values: props.profiles },
{ name: "thickness", values: props.thicknesses },
{ name: "ral", values: props.rals },
];
and a function selectItem that is called in the child component category-types-card.jsx
const selectItem = (item, category) => {
switch (category.name) {
case "category":
setSelectedCategoryItem(item);
break;
case "type":
setSelectedTypeItem(item);
break;
case "finish":
setSelectedFinishItem(item);
break;
case "profile":
setSelectedProfileItem(item);
break;
case "thickness":
setSelectedThicknessItem(item);
break;
case "ral":
setSelectedRalItem(item);
}
};
In the child component I need to show something for the categories that have active: true Category becomes active when it has an selectedItem.
I tried to make it active like categoryTypes[1] = { ...categoryTypes[1], active: true }; in the above switch, but in the child component the active property does not change for the certain category.
Calling the child component:
<Row className="mb-4">
{categoryTypes.map((category, index) => {
return (
<Colxx
xxs="12"
xs="6"
sm="6"
md="6"
lg="4"
xl="4"
xxl="4"
key={index}
>
<CategoryTypes
category={category}
employee={employee}
selectItem={selectItem}
selectedCategoryItem={selectedCategoryItem}
selectedTypeItem={selectedTypeItem}
selectedFinishItem={selectedFinishItem}
selectedProfileItem={selectedProfileItem}
selectedThicknessItem={selectedThicknessItem}
selectedRalItem={selectedRalItem}
/>
</Colxx>
);
})}
</Row>
How should I handle correctly this situation?
Thanks in advance!
How about passing a callback to the child component? Like so:
const ManageCategories = (props) => {
const [categoryTypes, setCategoryTypes] = useState([
{ name: "category", values: props.categories, active: true },
{ name: "type", values: props.types },
{ name: "finish", values: props.finishes },
{ name: "profile", values: props.profiles },
{ name: "thickness", values: props.thicknesses },
{ name: "ral", values: props.rals },
])
const setCategoryTypeActive = (categoryName, active) => {
setCategoryTypes((categoryTypes) =>
categoryTypes.map((categoryType) =>
categoryType.name === categoryName
? { ...categoryType, active }
: categoryType
)
)
}
return (
<Row className="mb-4">
{categoryTypes.map((category, index) => (
<Colxx xxs="12" xs="6" sm="6" md="6" lg="4" xl="4" xxl="4" key={index}>
<CategoryTypes
category={category}
employee={employee}
selectItem={selectItem}
selectedCategoryItem={selectedCategoryItem}
selectedTypeItem={selectedTypeItem}
selectedFinishItem={selectedFinishItem}
selectedProfileItem={selectedProfileItem}
selectedThicknessItem={selectedThicknessItem}
selectedRalItem={selectedRalItem}
setCategoryTypeActive={setCategoryTypeActive}
/>
</Colxx>
))}
</Row>
)
}
Then, somewhere in your child component, for example:
const CategoryTypesCard = (props) => {
props.setCategoryTypeActive("finish", true)
// ...
}
Related
I'm using Material-Table in a React app.
I enabled filtering, and after, I used Material-Table's filterComponent.
I have problems with retrieving the value from Column1FilterComponent custom component or pass down a function which can trigger a state change, and after it could trigger a fetch.
I placed my setFetchState(...) function to the point I'd like it to be.
Here is an example:
https://codesandbox.io/s/material-table-playground-forked-simple-q05vhf
Here are my questions:
How to get value from a filterComponent?
How to pass a function to filterComponent?
export function SomethingTable(props) {
const { fetchState, setFetchState } = props;
const [randomData, setRandomData] = useState([
{ filename: "1", another: "two" },
{ filename: "2", another: "three" },
{ filename: "3", another: "four" },
{ filename: "4", another: "five" },
{ filename: "5", another: "six"
}
]);
return (
<MaterialTable
title="Something"
columns={[
{
title: "Column1",
field: "filename",
type: "numeric",
width: "10%",
filterComponent: (props) => <Column1FilterComponent {...props} />
},
{ title: "Column2", field: "another" }
]}
data={randomData}
/*onFilterChange={(filters) => {
console.log("filters", filters);
}}*/
options={{
filtering: true
}}
/>
);
}
function Column1FilterComponent(props) {
const [value, setValue] = useState("");
return (
<TextField
id="area-text"
value={value}
onChange={(event) => {
setValue(event.target.value);
//My set function:
// setFetchState(event.target.value);
props.onFilterChanged(props.columnDef.tableData.id, event.target.value);
}}
/>
);
}
What I want is an example about how to make the drag and drop of my Table that works properly, but I cannot figure out how to make it works in (functional components)
My code :
function Preview() {
const { name } = useParams();
const [fieldsOfForm, setFieldsOfForm] = useState([]);
const [selectedForm, setSelectedForm] = useState([]);
const columns = [
{
title: 'Posição',
dataIndex: 'pos',
width: 30,
className: 'drag-visible',
render: () =>
<MenuOutlined style={{ cursor: 'grab', color: '#999' }}/>
},
{
title: "Form Name",
dataIndex: "field",
key: "field",
render: (text) => <a>{text}</a>,
},
{
title: "Tipo",
dataIndex: "fieldtype",
key: "fieldtype",
},
];
useEffect(() => {
let mounted = true;
let loadingStates = loading;
if (mounted) {
setFieldsOfForm(location.state);
loadingStates.allFields = false;
setLoading(false);
}
return () => (mounted = false);
}, [selectedForm]);
return (
//Some jsx....
<Row gutter={1}>
<Col span={1}></Col>
<Table dataSource={fieldsOfForm}
columns= {columns}/>
</Row>
// More jsx...
);
}
export default Preview;
Everything that I found on internet about this drag and drop from the lib of antd is in class component , but I wanted to make it works in my functional one.
Example that I found :
multi row drag-able table (antd)
I want some example in function component if someone has tried it already and could help me ?
Here's a functional working example:
import React from "react";
import "antd/dist/antd.css";
import { Table } from "antd";
import {
sortableContainer,
sortableElement,
sortableHandle
} from "react-sortable-hoc";
import { MenuOutlined } from "#ant-design/icons";
const data = [
{
key: "1",
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park",
index: 0
},
{
key: "2",
name: "Jim Green",
age: 42,
address: "London No. 1 Lake Park",
index: 1
},
{
key: "3",
name: "Joe Black",
age: 32,
address: "Sidney No. 1 Lake Park",
index: 2
},
{
key: "4",
name: "4",
age: 32,
address: "New York No. 1 Lake Park",
index: 3
},
{
key: "5",
name: "5",
age: 42,
address: "London No. 1 Lake Park",
index: 4
},
{
key: "6",
name: "6",
age: 32,
address: "Sidney No. 1 Lake Park",
index: 5
}
];
const DragHandle = sortableHandle(({ active }) => (
<MenuOutlined style={{ cursor: "grab", color: active ? "blue" : "#999" }} />
));
const SortableItem = sortableElement((props) => <tr {...props} />);
const SortableContainer = sortableContainer((props) => <tbody {...props} />);
function SortableTable() {
const [dataSource, setDataSource] = React.useState(data);
const [selectedItems, setSelectedItems] = React.useState([]);
const getColumns = () => {
return [
{
title: "Sort",
dataIndex: "",
width: 30,
className: "drag-visible",
render: (d, dd, i) => (
<>
<DragHandle active={selectedItems.includes(i)} />
</>
)
},
{
title: "Name",
dataIndex: "name",
className: "drag-visible"
},
{
title: "Age",
dataIndex: "age"
},
{
title: "Address",
dataIndex: "address"
}
];
};
const merge = (a, b, i = 0) => {
let aa = [...a];
return [...a.slice(0, i), ...b, ...aa.slice(i, aa.length)];
};
const onSortEnd = ({ oldIndex, newIndex }) => {
let tempDataSource = dataSource;
if (oldIndex !== newIndex) {
if (!selectedItems.length) {
let movingItem = tempDataSource[oldIndex];
tempDataSource.splice(oldIndex, 1);
tempDataSource = merge(tempDataSource, [movingItem], newIndex);
} else {
let filteredItems = [];
selectedItems.forEach((d) => {
filteredItems.push(tempDataSource[d]);
});
let newData = [];
tempDataSource.forEach((d, i) => {
if (!selectedItems.includes(i)) {
newData.push(d);
}
});
tempDataSource = [...newData];
tempDataSource = merge(tempDataSource, filteredItems, newIndex);
}
setDataSource(tempDataSource);
setSelectedItems([]);
}
};
const DraggableContainer = (props) => (
<SortableContainer
useDragHandle
disableAutoscroll
helperClass="row-dragging"
onSortEnd={onSortEnd}
{...props}
/>
);
const DraggableBodyRow = ({ className, style, ...restProps }) => {
// function findIndex base on Table rowKey props and should always be a right array index
const index = dataSource.findIndex(
(x) => x.index === restProps["data-row-key"]
);
return (
<SortableItem
index={index}
{...restProps}
selected={selectedItems.length}
onClick={(e) => {
if (e.ctrlKey || e.metaKey) {
selectedItems.includes(index)
? selectedItems.splice(selectedItems.indexOf(index), 1)
: selectedItems.push(index);
setSelectedItems(selectedItems);
} else {
setSelectedItems([]);
}
}}
/>
);
};
return (
<>
<h3>"CNTRL + Click" to select multiple items</h3>
<Table
pagination={false}
dataSource={dataSource}
columns={getColumns()}
rowKey="index"
components={{
body: {
wrapper: DraggableContainer,
row: DraggableBodyRow
}
}}
/>
{selectedItems.length ? <>{selectedItems.length} items selected </> : ""}
</>
);
}
You can play with it in Sandbox
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.
I am a novice at redux and i am trying to incorporate it in my code so the state can be managed more easily. I am trying to map through an array in my state but it is throwing the error below:
Uncaught TypeError: Cannot read property 'map' of undefined
this is the reducer for redux
const initialState = {
workoutlist: [
{
id: uuid.v4(),
name: 'Leg Day',
date: '08-09-2019',
duration: "60",
exerciselist: [
{
id: uuid.v4(),
exerciseName: 'Squats',
numberOfSets: "3",
reps: "12",
weight: "135",
},
{
id: uuid.v4(),
exerciseName: 'Leg press',
numberOfSets: "3",
reps: "10",
weight: "150",
},
{
id: uuid.v4(),
exerciseName: 'Lunges',
numberOfSets: "4",
reps: "12",
weight: "0",
},
],
selected: false,
},
{
id: uuid.v4(),
name: 'Running',
date: '08-11-2019',
duration: "40",
exerciselist: [],
selected: false,
},
],
disabled: true,
page: 1,
}
const workoutList = (state = initialState, action) => {
switch(action.type) {
// Returns whether the panel is selected or not
// and enables Start Workout button if only 1 is selected
case 'SELECT_PANEL':
state = {
workoutlist: state.workoutlist.map(workout => {
if (workout.id === action.id) {
workout.selected = !workout.selected
}
return workout;
})
}
var count = 0;
state.workoutlist.map((workout) => {
if (workout.selected === true) {
count = count + 1;
}
return count;
})
if (count !== 1) {
state = {
...state,
disabled: true
}
} else {
state = {
...state,
disabled: false
}
}
return state;
default:
return state;
}
}
this is the component that where the error is being thrown.
export default function WorkoutItem() {
const handleSelectedPanel = useSelector(state => state.workoutList);
const dispatch = useDispatch();
const { name, date, duration, exerciselist } = handleSelectedPanel;
return (
<ExpansionPanel style={styles.panel} onChange={() => dispatch(selectPanel())}>
<ExpansionPanelSummary>
<Typography variant="button" style={styles.header}>
{name}
</Typography>
<Typography variant="button" style={styles.header}>
{date}
</Typography>
<Typography align="right" style={styles.header}>
~{duration} mins
</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Table size="medium" style={styles.table}>
<TableHead>
<TableRow>
<TableCell padding="none" >Name</TableCell>
<TableCell padding="none" align="right"># of sets</TableCell>
<TableCell padding="none" align="right">average reps</TableCell>
<TableCell padding="none" align="right">weight</TableCell>
</TableRow>
</TableHead>
<TableBody>
{exerciselist.map((exercise) => (
<ExerciseList
key={exercise.id}
exercise={exercise}
/>
))}
</TableBody>
</Table>
<ExpansionPanelActions disableSpacing style={styles.actionButton}>
<EditWorkoutItem
workout={this.props.workout}
handleEditChange={this.props.handleEditChange}
/>
</ExpansionPanelActions>
</ExpansionPanelDetails>
</ExpansionPanel>
)
}
It is supposed to show a list of panels that can be expanded to show the contents but it throws an error saying the exerciselist is undefined in my state apparently.
Any help would be greatly appreciated!
Do you want to access the first item or workoutList ?. From your code handleSelectedPanel seams to be an array not an object. Is your destructuring correct?. Where is your useSelector?. What does console.log(handleSelectedPanel) gives you? Try
const { name, date, duration, exerciselist } = handleSelectedPanel[0];
or
const handleSelectedPanel = useSelector(state => state.workoutList[0]);
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;