How to create dynamic material-ui elements in a react app? - javascript

I am trying to create a dynamic mui grid element when a button is pressed in a react component. When I tried to create let newGrid = document.createElement('Grid') it does not work as a normal div creation.
Any suggestions?
Current code snippet:
return (
<>
<button onClick={addRow}>Add Row</button>
<Grid container className='v-timeline-container'>
<Grid xs={12} item className="v-timeline-item v-center v-border">
<TextItems />
</Grid>
</Grid>
</>
)
What I am trying to achieve dynamically:
return (
<>
<button onClick={addRow}>Add Row</button>
<Grid container className='v-timeline-container'>
<Grid xs={12} item className="v-timeline-item v-center v-border">
<TextItems />
</Grid>
<Grid xs={12} item className="v-timeline-item v-center v-border">
<TextItems />
</Grid>
</Grid>
</>
)

React uses a VDOM to keep track of stuff internally, you manipulating the DOM directly using DOM APIs is considered dangerous. What you should do this something like this.
function App(){
// you probably want to track the text items in each row,
// so use an array of objects
const [gridItems, setGridItems] = useState([]);
// this function adds a new grid item
const onClick = () => {
setGridItems([...gridItems, {text: "new row", id: gridItems.length + 1}]);
};
return (
<>
<button onClick={onClick}>Add Row</button>
<Grid container className='v-timeline-container'>
{
gridItems.map(item => (
<Grid xs={12} item className="v-timeline-item v-center v-border">
<TextItems>{item.text}</TextItems>
</Grid>
))
}
</Grid>
</>
);
}

Using setState is a solution.
It increments a number in state to create an array from it.
In your case you will certainly push new data to an array when you click on the button. But you get the idea.
import React, { useState } from 'react';
import { Grid } from '#mui/material'
const ListItems = () => {
const [items, setItems] = useState(1);
const addRow = () => {
setItems((prev) => prev + 1)
}
return (
<>
<button onClick={addRow}>Add Row</button>
<Grid container className='v-timeline-container'>
{
[...Array(items).keys()].map((key) => (
<Grid xs={12} item className="v-timeline-item v-center v-border">
<p>{key + 1} row</p>
</Grid>
))
}
</Grid>
</>
)
}
export default ListItems

Related

How to show list items after state changes in react-redux

I have a menu bar that shows category of my shop. When I click on a category a product list of that category should be shown on my shop component (also I use redux library). It works correctly for first time, but when I click on another category the state changes and the filter products update too but they don't show on my shop component. I must click a button on page to show updated list in shop component. How can I show them immediately after updating?
App.jsx
<Menubar/>
<Routes>
<Route path='/shopping' element={<Shop />}></Route>
</Routes>
Menubar.jsx
export default function MenuBar() {
const products = useSelector(state=>state.products);
const navigate = useNavigate();
const getCategory = (e) => {
let category = e.target.innerText;
dispatch(filterProByCategory(products, category));
navigate('/shopping');
}
return (
<>
<ul>
<li onClick={(e)=>getCategory(e)}>
Fashion
</li>
</ul>
</>
)
}
Shop.jsx
export default function Shop() {
const products = useSelector(state => state.filters.filterProducts);
const [filterProducts, setFilterproducts] = useState(products);
return (
<>
<Grid item xs={12} md={8} dir='rtl'>
<Card sx={{ minWidth: 275 }}>
<CardContent>
{/* */}
<Grid container spacing={1}>
{filterProducts && filterProducts.map((product, index) => (
<Grid item xs={12} md={4}>
<ProductCard product={product} key={index} />
</Grid>
))}
</Grid>
</>
)
}
Just use the direct result of products instead of using it for creating another state variable filteredProducts with useState
export default function Shop() {
const products = useSelector(state=>state.filters.filterProducts);
// const [filterProducts , setFilterproducts] = useState (products);
return (
<>
<Grid item xs={12} md={8} dir='rtl'>
<Card sx={{ minWidth: 275 }}>
<CardContent>
{/* */}
<Grid container spacing={1}>
{products && products.map((product , index)=>(
<Grid item xs={12} md={4}>
<ProductCard product={product} key={index}/>
</Grid>
))}
</Grid>
</>
)
}

Send selected text to another div in react JS using onSelect event?

I have a two div I want to send selected text to another div using onSelect event? Right now entire para sending to another div but I want to send just selected text. How can I do this?
Demo:- https://codesandbox.io/s/selected-text-send-to-another-div-using-onselect-0ccnrn?file=/src/App.js
My Code:-
import React from "react";
import { Box, Grid, TextField, Typography } from "#material-ui/core";
import { useState } from "react";
const SendSelectedText = () => {
const [label1, setlabel1]=useState('');
const [para, setPara]=useState('This is Old Para');
const handleClick = () => {
setlabel1(para);
};
return (
<>
<Box className="sendSelectedTextPage">
<Grid container spacing={3}>
<Grid item xl={6} lg={6} md={6}>
<textarea onSelect={handleClick}>{para}</textarea>
</Grid>
<Grid item xl={6} lg={6} md={6}>
<TextField
variant="outlined"
size="small"
label="Label One"
value={label1}
multiline
rows={3}
className="sendSelectedTextPageInput"
/>
</Grid>
</Grid>
</Box>
</>
);
};
export default SendSelectedText;
Thanks for your support!
All you need is use window.getSelection().
Here is solution
import React from "react";
import { Box, Grid, TextField, Typography } from "#material-ui/core";
import { useState } from "react";
const SendSelectedText = () => {
const [label1, setlabel1] = useState("");
const [para, setPara] = useState("This is Old Para");
const handleMouseUp = () => {
setlabel1(window.getSelection().toString()); // setting up label1 value
};
return (
<>
<Box className="sendSelectedTextPage">
<Grid container spacing={3}>
<Grid item xl={6} lg={6} md={6}>
// changed event to onMouseUp
<textarea onMouseUp={handleMouseUp}>{para}</textarea>
</Grid>
<Grid item xl={6} lg={6} md={6}>
<TextField
variant="outlined"
size="small"
label="Label One"
value={label1}
multiline
rows={3}
className="sendSelectedTextPageInput"
/>
</Grid>
</Grid>
</Box>
</>
);
};
export default SendSelectedText;
Sandboxlink
You have to use the selection
const handleClick = (e) => {
setlabel1(
e.target.value.substring(e.target.selectionStart, e.target.selectionEnd)
);
};
or
const handleClick = (e) => {
setlabel1(
para.substring(e.target.selectionStart, e.target.selectionEnd)
);
};
Based on this
sandbox

how can i append selected row data in another table

what my task is I am using a table with two different conditions like in the first table whatever data coming I will show that in the first table and in the second table whatever row I select in the first table that I want to show in the second table as the second table I called select summary so my task is in the first table whatever row I selected in need to how that row in the second table I am using same table component for this for better you can see CodeSandBox link
import React, { useState, useMemo, useEffect } from "react";
import {
Grid,
makeStyles,
CardContent,
Card,
Box
} from "#material-ui/core";
import EnhancedTable from "./EnhancedTable";
const useStyles = makeStyles((theme) => ({
root: {
padding: theme.spacing(0, 2, 2),
},
formGrid: {
padding: theme.spacing(2),
},
cardColor: {
borderColor: "#0bb7a7",
},
}));
function AddToExclusionList() {
const classes = useStyles();
const [sanctionsList, setSanctionsList] = useState([]);
const updateListsRow = ({ index, value, row }, listType) => {
switch (listType) {
case "sanctions":
setSanctionsList((prevState) => {
prevState[index].status = value;
return [...prevState];
});
break;
default:
}
};
return (
<Grid className={classes.root}>
<Grid item xs={12} style={{ textTransform: "capitalize" }}>
<Card className={classes.cardColor} variant="outlined">
<CardContent>
<>
<EnhancedTable
show={true}
step="first"
/>
</>
</CardContent>
</Card>
</Grid>
<Box mt={3}></Box>
<Grid item xs={12} style={{ textTransform: "capitalize" }}>
<Card className={classes.cardColor} variant="outlined">
<CardContent>
<>
<h3>summary table</h3>
<EnhancedTable
checkboxToggle={(rowDetails) => {
updateListsRow(rowDetails, "sanctions");
}}
/>
</>
</CardContent>
</Card>
</Grid>
</Grid>
);
}
export default AddToExclusionList;
CodeSandBox Link
You've achieved your goal very weird! Anyway, based on your code in codesandbox. You need to add a state to AddToExclusionList component, like this:
const [newRows, setNewRows] = useState([]);
const setSummaryRows = (selectedRows) => {
const copy = [...rows];
const filteredRows = copy.filter((x) => selectedRows.includes(x.name));
setNewRows(filteredRows);
};
We need the mentioned state to update the summary table's rows.
Also add rows and setNewRows prop to EnhancedTable and give it rows from out of the component. In addition move rows and createData to App.js. So you should use EnhancedTable in App.js same as bellow:
<Grid className={classes.root}>
<Grid item xs={12} style={{ textTransform: "capitalize" }}>
<Card className={classes.cardColor} variant="outlined">
<CardContent>
<>
<h3>first table</h3>
<EnhancedTable
// passing data for rendering table according condition
step="first"
rows={rows}
setNewRows={(selected) => {
setSummaryRows(selected);
}}
/>
</>
</CardContent>
</Card>
</Grid>
<Box mt={3}></Box>
<Grid item xs={12} style={{ textTransform: "capitalize" }}>
<Card className={classes.cardColor} variant="outlined">
<CardContent>
<>
<h3>summary table</h3>
<EnhancedTable
// trying to pasing selected data
rows={newRows}
setNewRows={() => {}}
/>
</>
</CardContent>
</Card>
</Grid>
</Grid>
And the last part is using useEffect based on selected in EnhancedTable component:
useEffect(() => {
setNewRows(selected);
}, [selected]);

array.map() does not render items horizontally in a grid container in React

I'm using React and material-ui.
My goal is to render a grid container, starting from an external javascript file, that exports its own array. This grid must have 3 items per row, but at the moment it just renders the items on a single column.
Here is the code:
import React from "react";
import CoffeeCard from "./CoffeeCard";
import { Grid } from "#material-ui/core";
import files from "./constants";
function Content() {
return (
<Grid direction="rows" container spacing={2}>
<Grid item xs={12} sm={4}>
{files.map((obj) => {
return (
<CoffeeCard
title={obj.title}
price={obj.price}
description={obj.description}
avatarUrl={obj.avatarUrl}
imgSrc={obj.imgSrc}
/>
);
})}
</Grid>
</Grid>
);
}
export default Content;
You need to move the nested Grid component for individual items inside the map function. Right now your code is only rendering 1 Grid item component with children , you need to render one for each row:
<Grid direction="rows" container spacing={2}>
{files.map((obj) => {
return (
<Grid item xs={12} sm={4}>
<CoffeeCard
title={obj.title}
price={obj.price}
description={obj.description}
avatarUrl={obj.avatarUrl}
imgSrc={obj.imgSrc}
/>
</Grid>
);
})}
</Grid>
</Grid>
The issue is how you are using the <Grid /> component from Material UI. Please refer to its documentation on Grid for more info.
Notably, you want your item to wrap each individual item. As you have it, you have a single item that wraps your content.
So just move your <Grid item> into your .map return value:
import React from 'react';
import CoffeeCard from './CoffeeCard';
import { Grid } from '#material-ui/core';
import files from './constants';
function Content() {
return (
<Grid direction="rows" container spacing={2}>
{files.map((obj) => {
return (
<Grid item xs={12} sm={4}>
<CoffeeCard
title={obj.title}
price={obj.price}
description={obj.description}
avatarUrl={obj.avatarUrl}
imgSrc={obj.imgSrc}
/>
</Grid>
);
})}
</Grid>
);
}
export default Content;

Auto scroll react redux implementation

I am implementing auto-scroll option for my application. In below case channels are list of data which fetch from database. I used redux to call api. how can i connect InfiniteScroll and channels list to get auto-scroll feature?
import React, { Fragment } from "react";
import { Grid } from "#material-ui/core";
import ChannelCard from "./Card";
import CreateChannel from "./Create";
import SimpleSelect from "./Filter";
import InfiniteScroll from "react-infinite-scroll-component";
const ChannelList = ( {channels: { channels}}) => {
const [data, setData] = React.useState({
items: Array.from({ length: 20 })
});
const { items } = data;
const fetchMoreData = () => {
// a fake async api call like which sends
// 20 more records in 1.5 secs
setTimeout(() => {
setData({
items: items.concat(Array.from({ length: 20 }))
});
}, 1500);
};
//view
const view = (
<Fragment>
<Grid container>
<Grid item xs={6} sm={6} md={10} lg={10} xl={10}></Grid>
<Grid>
<SimpleSelect />
</Grid>
</Grid>
<Fragment>
<InfiniteScroll
dataLength={items.length}
next={fetchMoreData}
hasMore={true}
loader={<h4>Loading...</h4>}
>
<Grid container>
{channels.map(channel => (
<Grid key={channel._id} item xs={6} sm={6} md={3} lg={2} xl={2}>
<ChannelCard
channel={channel}
isAuthenticated={isAuthenticated}
/>
</Grid>
))}
</Grid>
</InfiniteScroll>
</Fragment>
</Fragment>
);
return <Fragment>{view}</Fragment>;
};
export default ChannelList;
(I used redux to call api. how can i connect InfiniteScroll and channels list to get auto-scroll feature?)
InfiniteScroll component requires the children to be the array of components that you want to have infinite scrolling on, since you have only one child which is the Grid component it is doing its job for only one item hence its not working: https://www.npmjs.com/package/react-infinite-scroll-component
just remove the wrapping Grid component
<InfiniteScroll
dataLength={items.length}
next={fetchMoreData}
hasMore={true}
loader={<h4>Loading...</h4>}
>
{channels.map(channel => (
<Grid key={channel._id} item xs={6} sm={6} md={3} lg={2} xl={2}>
<ChannelCard
channel={channel}
isAuthenticated={isAuthenticated}
/>
</Grid>
))}
</InfiniteScroll>

Categories

Resources