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]);
Related
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>
</>
)
}
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
I am working with the map function in react and I am getting all my info to map except for my button ref. Below is the code. The data is getting pulled from a database and passed as a prop as well. When I filter the list I get the look I am looking for but when I go to select a doctor I only get the last name for the doctor that was the last item iterated, and this is across all the buttons when I click them. Where am I going wrong if I may ask.
import {
Button,
Card,
CardActions,
CardContent,
Grid,
Typography,
} from "#material-ui/core";
import { makeStyles } from "#material-ui/styles";
import { useRef } from "react";
const styles = makeStyles({
bound: {
margin: "40px 200px",
position: "relative",
},
card: {
maxWidth: "70%",
margin: "50px auto",
borderStyle: "solid",
borderRadius: "50px",
borderColor: "black",
borderWidth: "3px",
"&:hover": {
boxShadow: "black",
borderColor: "green",
},
},
null: {
display: "none",
},
btn: {
margin: "20px 0 0 0",
borderRadius: "20px",
"&:hover": {
backgroundColor: "green",
color: "white",
},
},
});
const SearchResult = (props) => {
const doctorList = props.doctor;
const selectedRef = useRef();
const selectedHandler = (e) => {
console.log(e.target.target);
console.log(selectedRef.current.value);
};
const doctorCard = (doctorList) => {
return (
<Card
variant="outlined"
className={`${doctorList ? classes.card : classes.null}`}
key={doctorList.user_id}
id={doctorList.user_id}
>
<CardContent>
<Grid container>
<Grid xs={6} md={6} lg={6} item>
<img src="#" alt="Img.png" />
</Grid>
<Grid xs={6} md={6} lg={6} item>
<Typography variant="h6">
Dr. {doctorList.first_name} {doctorList.last_name}
</Typography>
<Typography variant="h6">
Speciality: {doctorList.title}
</Typography>
<Typography variant="h6">
Primary Language: {doctorList.primary_language}
</Typography>
</Grid>
</Grid>
<CardActions>
<Button
variant="contained"
className={classes.btn}
type="submit"
ref={selectedRef}
value={doctorList.last_name}
onClick={selectedHandler}
size="large"
fullWidth
>
Select
</Button>
</CardActions>
</CardContent>
</Card>
);
};
const classes = styles();
return (
<div className={classes.bound}>{doctorList.doctor.map(doctorCard)}</div>
);
};
export default SearchResult;
OUTPUT
You are looping list and using common reference 'selectedRef' in you list component. Once list rendering finishes, last_name of last item in your array is being stored in 'selectedRef' reference. That's why whenever you click it's always getting last item.
To resolve this you might want to break component on granular level. Use two component ItemList and ListItem. ItemList will simply loop array and in ListItem component create reference to each item while looping. That should resolve your issue.
const ItemList = (props) => {
const renderList=(doctor)=>{
return <ListItem key={`doctor-${doctor.id}`} {...doctor}/>
}
return <div>{props.data.map(renderList)}</div>
}
const ListItem = () => {
// reference below create new reference for every item
const selectedRef = useRef();
// rest of logic to render doctor card
}
I've a list of countries which i would like show some info related to the country. I run a loop in order to show the info for each country. I've created a component for a country.
const CountryCard = ({ info }) => {
const { country, infoForLastDate, path } = info
return (
<Card >
<CardContent>
<Typography variant="h5" component="h2" >
{country.name}
</Typography>
<Flag oneToOne={country.oneToOne} fourToThree={country.fourToThree} />
<Typography variant="body2" >
Confirmed: {infoForLastDate.confirmed}
</Typography>
<Typography variant="body2" component="p">
Recovered: {infoForLastDate.recovered}
</Typography>
<Typography variant="body2" component="p">
Deaths: {infoForLastDate.deaths}
</Typography>
</CardContent>
<CardActions>
<Link href={path}>
See more
</Link>
</CardActions>
</Card>
)
}
export default CountryCard
Also, I've created another component to show the flag related to the country.
import React from 'react'
import { imageUrlFor } from '../lib/image-url'
const Flag = ({ oneToOne, fourToThree }) => {
const url = imageUrlFor(oneToOne.asset._id)
return (
<img src={url} />
)
}
export default Flag
I get the error TypeError: oneToOne is null
i don't know why it is like the flags render twice times. when I debugged, first time oneToOne property has a value, but at the end is run again and is null
Why is it happens?
Edit: Add CountryList component:
const CountryList = ({list}) => {
return (
<Grid container spacing={3}>
{ list.length > 1 && list.map(country => {
const countryWithPath = {
...country,
path: `/country/${country.country.name.toLowerCase().replace(' ', '-').replace('*', '')}`
}
return (
<Grid item xs={12} sm={6} key={country._id} >
<CountryCard info={countryWithPath} />
</Grid>)
})
}
</Grid>
)
}
export default CountryList
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>