Chart Data shows it's never updated through my setState variable - javascript

I have a popup dialog where I get a bunch of values from the user and then get a response after making an API request. I put an inline conditional rendering on the dialog box as it should only render once chart data is updated from the response. However, the dialog never appears even if console.log shows the data is updated. I tried to use useEffect() with many functions but it did not work. Any idea how to refresh the data again?
Edit: Added only relevant code
const [barGraphData, setBarGraphData] = useState([]);
const funcSetBarGraphData = (newBarGraphData) => {
setBarGraphData(newBarGraphData);
};
const sendChartData = async () => {
let bar_response = await axios.post(
"http://localhost:8080/h2h-backend/bardata",
bar_data,
{headers: {'Content-Type': 'application/json'}}
).then(res=>{
const resData = res.data;
const resSubstring = "[" + resData.substring(
resData.indexOf("[") + 1,
resData.indexOf("]")
) + "]";
const resJson = JSON.parse(resSubstring);
console.log(typeof resJson, resJson);
funcSetBarGraphData(barGraphData);
}).catch(err=>{
console.log(err);
});
chartClickOpen();
};
Returning popup dialog with charts when button is clicked:
<StyledBottomButton onClick={sendChartData}>Submit</StyledBottomButton>
{barGraphData.length > 0 && <Dialog
fullScreen
open={openChart}
onClose={chartClickClose}
TransitionComponent={Transition}
>
<AppBar sx={{ position: 'relative' }}>
<Toolbar>
<Typography sx={{ ml: 2, flex: 1 }} variant="h6" component="div">
Analytics View
</Typography>
<IconButton
edge="start"
color="inherit"
onClick={chartClickClose}
aria-label="close"
>
<CloseIcon />
</IconButton>
</Toolbar>
</AppBar>
<Grid container spacing={2}>
<Grid item xs={8} sx={{ pt: 2 }}>
<BarChart width={730} height={250} data={barGraphData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="business_name" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="num_of_customers" fill="#8884d8" />
<Bar dataKey="sum_total_amount" fill="#82ca9d" />
</BarChart>
{/* <Bar options={set_bar.bar_options} data={set_bar.bar_data} redraw={true}/> */}
</Grid>
<Grid item xs={4} sx={{ pt: 2 }}>
{/* <Pie data={data2} /> */}
</Grid>
</Grid>
</Dialog>}
<StyledBottomButton onClick={handleClose}>Cancel</StyledBottomButton>

Related

uneven spacing when using material ui grid

In both the images the spacing on the right is more,I'm not using any custom css to alter the padding or margin
the Mui grid system seems to give me this result
spacing on the right
same on mobile screen
code for grid
const MovieCard = (props) => {
const lengthSettler = (text) => {
if (text.length > 301) {
return `${text.substring(0, 300)}...Read More`;
} else {
return text;
}
};
const print = () => {
console.log('MovieCard', props);
};
print();
return (
<Grid container direction={'row'} justify="center" spacing={6}>
{props.movies.map((movie) => (
<Grid item xs={4}>
<Card sx={{ maxWidth: 345, backgroundColor: '#ece3e3' }}>
<CardActionArea>
{movie.poster_path && (
<CardMedia
component="img"
image={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`}
alt={movie.title}
/>
)}
{!movie.poster_path && (
<CardMedia
component="img"
image="https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/No-Image-Placeholder.svg/1665px-No-Image-Placeholder.svg.png"
alt={movie.title}
/>
)}
<CardContent>
<Typography gutterBottom variant="h5" component="div">
{movie.title}
</Typography>
{movie.overview && (
<Typography variant="body2" color="text.secondary">
{lengthSettler(movie.overview)}
</Typography>
)}
</CardContent>
</CardActionArea>
</Card>
</Grid>
))}
</Grid>
);
};
export default MovieCard;
things I tried
adding justify space-between
removing all the grid items
reinstalling mui

Custom Pagination in Material-Table

I want to override the default pagination in Material-Table to look different, but keep the same default elements there. My override allows me to move between pages in the table, but I cannot change how many rows appear in the table. I cannot skip to the last or first page either. I want to keep the exact same options as what is there by default, but change how they look.
All of my Icon, Grid, Typography, etc, imports are #material-ui/core/ or #material-ui/icons/
function IntakeList() {
const CustomPaginationComponent = (props) => {
const { page, rowsPerPage, count, onChangePage } = props;
let from = rowsPerPage * page + 1;
let to = rowsPerPage * (page + 1);
if (to > count) {
to = count;
}
return (
<td>
<Grid container alignItems="center" style={{ paddingTop: 8 }}>
<Grid item>
<IconButton disabled={page === 0} onClick={(e) => onChangePage(e, page - 1)}>
<SkipPreviousIcon fontSize="small" color={page === 0 ? "disabled" : "primary"} />
<Typography>Prev</Typography>
</IconButton>
</Grid>
<Grid item>
<Typography variant="caption" style={{ color: "black" }}>
{from}-{to} of {count}
</Typography>
</Grid>
<Grid item>
<IconButton disabled={to >= count} onClick={(e) => onChangePage(e, page + 1)}>
<Typography>Next</Typography>
<SkipNextIcon fontSize="small" color={to < count ? "primary" : "disabled"} />
</IconButton>
</Grid>
</Grid>
</td>
);
};
return (
<MaterialTable
title="Title"
data={data}
columns={columns}
options={{
pageSize: 10,
pageSizeOptions: [10, 15, 25, 50, 100],
}}
components={{
Pagination: (props) => {
return <CustomPaginationComponent {...props} />;
},
}}
/>
);
}
What you have done so far is right. You just simply have to code for the "skip to last page", "skip to first page" and "select row options" just like you did for "next" and "previous" page.
https://codesandbox.io/s/solution-q1cmer?file=/src/App.js
When you console log the props of pagination you will get this
Using the rowsPerPageOptions , onChangeRowsPerPage and some of the code you have coded I was able to code the number of rows per page.
To be honest I don't think you have to code to next page and previous page functionality. It's already in props. You simply has to declare them in the right place.
Happy Coding

Calling onClick inside map function

I have a custom Input Box and when I type inside a custom input component It'll re-render the typed input inside.
import {
Badge,
Box,
CloseButton,
Grid,
GridItem,
Input,
Text
} from "#chakra-ui/react";
import React, { useEffect, useState } from "react";
function InputTag(props) {
const [tags, setTags] = useState(props.values);
const removeTag = (index) => {
setTags(tags.filter((_, i) => i !== index));
};
const addTag = (event) => {
if (event.target.value !== "") {
setTags([...tags, event.target.value]);
props.setFieldValue("tags", [...tags, event.target.value]);
event.target.value = "";
}
};
useEffect(() => {
props.show === false && setTags([]);
}, [props.show]);
//update values based on click suggestions
useEffect(() => {
setTags([props.values, props.suggTag]);
}, [props.suggTag, props.values]);
return (
<Box
display={"flex"}
border="1px"
borderColor={"gray.200"}
borderRadius={"md"}
padding={2}
>
<Grid templateColumns="repeat(3, 1fr)" gap={2} overflow="visible">
{tags &&
tags.map((tag, index) => (
<GridItem key={index}>
<Badge
variant="solid"
colorScheme={"purple"}
display={"flex"}
borderRadius="full"
justifyContent="space-between"
alignItems="center"
gap={2}
>
<Text>{tag}</Text>
<CloseButton onClick={() => removeTag(index)} />
</Badge>
</GridItem>
))}
</Grid>
<Input
type="text"
name="tags"
id="tags"
variant={"unstyled"}
placeholder="Add Tag"
_placeholder={{ fontsize: "md" }}
onChange={props.handleChange}
onBlur={props.handleBlur}
onError={props.errors}
onKeyUp={(event) =>
event.key === "Enter" ? addTag(event) && event.preventDefault() : null
}
/>
</Box>
);
}
export default InputTag;
Here, when I hit enter It'll render them inside the custom Input Box
I Inserted a custom array of strings as "ex_Tag" inside Previewer.js so that when I click on the word in array, it'll also get rendered inside custom input as well.
function NewUploader({ isOpen, onClose }) {
const cancelRef = useRef();
const ex_tags = ["Design", "Strategy", "Human Centered Design"];
const [show, Setshow] = useState(true);
const [suggTag, setSuggTag] = useState();
const initialValues = {
files: null,
tags: []
};
const validationSchema = yup.object({
files: yup.mixed().required("File is Required"),
tags: yup.mixed().required("tags required")
});
const onSubmit = (values, actions) => {
const formData = new FormData();
formData.append("files", values.files[0]);
formData.append("tags", values.tags);
for (var pair of formData.entries()) {
console.log(pair[0] + ", " + pair[1]);
}
actions.setSubmitting(false);
actions.resetForm();
Setshow(!show);
onClose();
};
const handlethis = (e) => {
e.preventDefault();
};
//insert suggested word to useState so i can pass it to custom input
const handleClick = (tag) => {
setSuggTag(tag);
};
return (
<Modal isOpen={isOpen} onClose={onClose} isCentered>
{/* update code on model here */}
<ModalOverlay />
<ModalContent>
<ModalHeader>
<Text fontWeight={"bold"} color="gray.900">
Upload Buddy
</Text>
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Flex direction="column" gap={3}>
<Box>
<Text fontWeight={"normal"} color="gray.700">
This learning contentwill not be summarised. to summarize your
content, use{" "}
<Link color={"purple.400"}>Create Knowledge Nugget</Link> option
instead.
</Text>
</Box>
<Box>
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
validationSchema={validationSchema}
>
{(formik) => (
<Form
onSubmit={handlethis}
autoComplete="off"
encType="multipart/form-data"
>
<FormLabel htmlFor="file">
<Text
fontSize="sm"
fontWeight="normal"
color="gray.900"
fontFamily={"body"}
>
Upload files
</Text>
</FormLabel>
{/* drag droop sec */}
{formik.isSubmitting ? (
<>
<Grid
templateColumns="repeat(3, 1fr)"
gap={2}
overflow="hidden"
>
{formik.values.files &&
formik.values.files.map((file, index) => (
<GridItem key={index}>
<Badge
variant="solid"
borderRadius="xl"
colorScheme={"gray"}
w={file.name.length * 4}
h="8"
display="flex"
justifyContent="center"
alignItems="center"
my={2}
>
<Text fontFamily={"body"}>{file.name}</Text>
<CloseButton colorScheme={"blackAlpha"} />
</Badge>
</GridItem>
))}
</Grid>
<Progress colorScheme={"yellow"} isIndeterminate />
</>
) : (
<>
<Dragdrop setFieldValue={formik.setFieldValue} />
<Grid
templateColumns="repeat(3, 1fr)"
gap={2}
overflow="hidden"
>
{formik.values.files &&
formik.values.files.map((file, index) => (
<GridItem key={index}>
<Badge
variant="solid"
borderRadius="xl"
colorScheme={"gray"}
w={file.name.length * 4}
h="8"
display="flex"
justifyContent="space-between"
alignItems="center"
my={2}
>
<Text fontFamily={"body"}>{file.name}</Text>
<CloseButton colorScheme={"blackAlpha"} />
</Badge>
</GridItem>
))}
</Grid>
{formik.errors.files && formik.touched.files && (
<Text fontFamily={"body"} color="red">
{formik.errors.files}
</Text>
)}
</>
)}
<FormErrorMessage>
<ErrorMessage name="file" />
</FormErrorMessage>
<FormLabel htmlFor="tags">
<Text
fontSize="sm"
fontWeight="normal"
color="gray.900"
fontFamily={"body"}
>
Tags
</Text>
</FormLabel>
<InputTag
setFieldValue={formik.setFieldValue}
handleChange={formik.handleChange}
handleBlur={formik.handleBlur.call}
values={formik.values.tags}
show={show}
suggTag={suggTag}
/>
{formik.errors.tags && formik.touched.tags && (
<Text fontFamily={"body"} color="red">
{formik.errors.tags}
</Text>
)}
<FormErrorMessage>
<ErrorMessage name="tags" />
</FormErrorMessage>
<Box
aria-invalid="true"
display={"flex"}
flexDir="row"
gap={2}
my={2}
>
<Text fontFamily={"body"}>Suggested</Text>
<Grid
templateColumns="repeat(3, 1fr)"
gap={2}
overflow="hidden"
>
{ex_tags.map(
(tag, index) => (
<GridItem key={index}>
//I inserted on click call here
<Box onClick={handleClick(tag)}>
<Badge
variant={"subtle"}
borderRadius="lg"
colorScheme={"gray"}
_hover={{
cursor: "pointer",
bgColor: "gray.200"
}}
>
<Text fontFamily={"body"}>{tag}</Text>
</Badge>
</Box>
</GridItem>
),
this
)}
</Grid>
</Box>
<Box display={"flex"} justifyContent="center" my={3}>
<Button
type="button"
ref={cancelRef}
colorScheme="yellow"
isLoading={formik.isSubmitting}
onClick={formik.handleSubmit}
>
<Text
fontWeight="bold"
fontSize="18px"
color="gray.900"
fontFamily={"body"}
>
Submit
</Text>
</Button>
</Box>
</Form>
)}
</Formik>
</Box>
</Flex>
</ModalBody>
</ModalContent>
</Modal>
);
}
export default NewUploader;
but It seems when I render them to the screen it will come out as I triggered the onClick even though I didn't.
For now I commented out the useEffect func inside input component
I have uploaded it to code sandbox Bellow.
https://codesandbox.io/s/amazing-heyrovsky-9kr0ex?file=/src/Previewer.js

Material ui Dialog onClick deletes only last index

So I've created a get request, which shows all the exercises and the options to edit (which will redirect to another url) and delete. I wanted to use material ui's Dialog, so there will be a confirmation before deleting. The problem is that, say I have 3 exercises and want to delete exercise with index 1, when click the on delete button it appears the last index in the element i.e the last exercise, although I want to delete the second. How can I fix that?
And also can I move Dialog in another file for more clear coding?
Exercises.js
const Exercises = () => {
const { categories, deletedCategories, error, isLoading, exercises, open, handleClickOpen, handleClose, deleteExercise } = useFormExercises();
const [showCategories, setShowCategories] = useState(false);
if (isLoading) {
return <div>Loading...</div>
}
if (error) {
return <div>There was an error: {error}</div>
}
return (
<Grid container className='content' >
<Card className='exerciseCard'>
<h1>Exercises</h1>
<FormControlLabel sx={{ width:'500px'}}
label="Show Categories"
control={
<Checkbox
sx={{ml:2}}
checked={showCategories}
onChange={(event) => setShowCategories(event.target.checked)}
/>
}
/>
<Button sx={{pr:6}} variant='fab' href={('/exercises/add')}>
<Add />
</Button>
<Divider />
<Grid className="exerciseContent" >
{exercises.map(exercise => (
<Card>
<tr key={exercise.id}>
<List>
<ListItem
key={exercise.id}
secondaryAction={
<IconButton onClick={handleClickOpen}>
<Delete />
</IconButton>
}>
<Button
sx={{mr:4}}
href={(`/exercises/edit/${exercise.id}`)} >
<Edit />
</Button>
<ListItemText sx={{justifyContent:'center', width:'400px'}}
primary={exercise.exercise_name}
secondary={showCategories ? exercise["exerciseCategories.category_name"] : null}
/>
</ListItem>
<Dialog
open={open}
onClose={handleClose}
style={{ borderColor: 'red' }}
>
<Box sx={{ borderTop: 3, color: 'red' }}>
<DialogTitle sx={{ color: 'black', backgroundColor: 'gainsboro', pl: 11 }}>
Delete Exercise
</DialogTitle>
<DialogContent>
<DialogContentText color='black' >
<Warning fontSize='large' color='error' id='warning' />
Are you sure you want to delete the exercise: {exercise.exercise_name} ?
</DialogContentText>
</DialogContent>
<DialogActions >
<Button variant='contained' onClick={() => deleteExercise(exercise.id)} autoFocus>
Yes
</Button>
<Button variant='outlined' onClick={handleClose}>No</Button>
</DialogActions>
</Box>
</Dialog>
</List>
</tr>
</Card>
))}
</Grid>
</Card>
</Grid>
)
}
export default Exercises
My delete function
useFormExercises.js
...
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
}
const deleteExercise = async (id) => {
try {
await fetch(`${BASE_URL}/exercises/${id}`, {
method: "DELETE",
}).then(response => {
setExercises(exercises.filter(exercise => exercise.id !== id))
return response.json()
})
} catch (error) {
console.log(error)
}
}
And if another thing seems of, please share your opinion!
Thanks in advance!

Recharts show tooltip on ALL charts on hover

I'm using recharts/ react to visualize some simple data, and running into a wall. I want to show the line + tooltip on ALL the graphs whenever a user hovers over any of the graphs. Been trying to use state or dispatch but running into a wall.
I've attached code snippets for my chart and dashboard files with the attempt at using dispatcher. I dont't know where in chart.js to actually call showTooltip. The functionality I want is to show the tooltips for all charts whenever a user hovers over any single chart. If one tooltip = active, I want all tooltips=active. Any guidance would be super helpful!
chart.js snippet
export default function Chart(props) {
const {state, dispatch} = useContext(AppContext);
const showTooltip = (newValue) => {
dispatch({ type: 'HOVER', data: newValue,});
};
const theme = createMuiTheme({
palette: {
primary: {
main: '#041f35'
},
secondary: {
main: '#5dc5e7'
}
}
});
return (
<React.Fragment>
<MuiThemeProvider theme={theme}>
<Title>{props.title}</Title>
<ResponsiveContainer width="100%" height="100%">
<LineChart
width={500}
height={300}
data={data}
margin={{
top: 5,
right: 5,
left: -35,
bottom: 5,
}}
>
<XAxis dataKey="time" />
<YAxis axisLine={false} tickLine={false}/>
<Tooltip />
<CartesianGrid vertical={false} stroke="#d3d3d3"/>
<Line type="monotone" dataKey="mktplace1" stroke={theme.palette.primary.main} activeDot={{ r: 8 }} />
<Line type="monotone" dataKey="mktplace2" stroke={theme.palette.secondary.main} />
</LineChart>
</ResponsiveContainer>
</MuiThemeProvider>
</React.Fragment>
);
}
dashboard.js file snippet
export const AppContext = React.createContext();
// Set up Initial State
const initialState = {
active: new Boolean(false),
};
function reducer(state, action) {
switch (action.type) {
case 'HOVER':
return {
active: new Boolean(true)
};
default:
return initialState;
}
}
export default function Dashboard() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div className={classes.root}>
<CssBaseline />
<main className={classes.content}>
<Container maxWidth="lg" className={classes.container}>
<Grid container spacing={2}>
{/* Chart */}
<Grid item xs={12} sm={12} md={6} lg={6} xl={6}>
<Paper className={fixedHeightPaper}>
<AppContext.Provider value={{ state, dispatch }}>
<Chart title="Sales by Marketplace"/>
</AppContext.Provider>
</Paper>
</Grid>
<Grid item xs={12} sm={12} md={6} lg={6} xl={6}>
<Paper className={fixedHeightPaper}>
<AppContext.Provider value={{ state, dispatch }}>
<Chart title="Sales by Marketplace"/>
</AppContext.Provider>
</Paper>
</Grid>
<Grid item xs={12} sm={12} md={6} lg={6} xl={6}>
<Paper className={fixedHeightPaper}>
<AppContext.Provider value={{ state, dispatch }}>
<Chart title="Sales by Marketplace"/>
</AppContext.Provider>
</Paper>
</Grid>
<Grid item xs={12} sm={12} md={6} lg={6} xl={6}>
<Paper className={fixedHeightPaper}>
<AppContext.Provider value={{ state, dispatch }}>
<Chart title="Sales by Marketplace"/>
</AppContext.Provider>
</Paper>
</Grid>
</Grid>
</Container>
</main>
</div>
);
}
I know this was a long time ago, but there is a "syncId" property to put on your chart. All charts with the same syncId will show tooltips at the same time.

Categories