React.js, Material Ui Stepper direction - javascript

I have a weird situation right here where I try to build a stepper component within my app.
everything goes well so far but I try to make this stepper to go from Right to left.
things I have tried:
I tried to instal i18next (translation library) and it didn't work out.
I tried to go from 3 down to 2, and it also is not a satisfying result
here is the result:
and here is my code:
import {
makeStyles,
Theme,
createStyles,
withStyles,
} from '#material-ui/core/styles';
import clsx from 'clsx';
import Stepper from '#material-ui/core/Stepper';
import Step from '#material-ui/core/Step';
import StepLabel from '#material-ui/core/StepLabel';
import SettingsIcon from '#material-ui/icons/Settings';
import GroupAddIcon from '#material-ui/icons/GroupAdd';
import VideoLabelIcon from '#material-ui/icons/VideoLabel';
import StepConnector from '#material-ui/core/StepConnector';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import { StepIconProps } from '#material-ui/core/StepIcon';
const ColorlibConnector = withStyles({
alternativeLabel: {
top: 22,
},
active: {
'& $line': {
backgroundImage:
'linear-gradient( 95deg,rgb(242,113,33) 0%,rgb(233,64,87) 50%,rgb(138,35,135) 100%)',
},
},
completed: {
'& $line': {
backgroundImage:
'linear-gradient( 95deg,rgb(242,113,33) 0%,rgb(233,64,87) 50%,rgb(138,35,135) 100%)',
},
},
line: {
height: 3,
border: 0,
backgroundColor: '#eaeaf0',
borderRadius: 1,
},
})(StepConnector);
const useColorlibStepIconStyles = makeStyles({
root: {
backgroundColor: '#ccc',
zIndex: 1,
color: '#fff',
width: 50,
height: 50,
display: 'flex',
borderRadius: '50%',
justifyContent: 'center',
alignItems: 'center',
},
active: {
backgroundImage:
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)',
},
completed: {
backgroundImage:
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
},
});
function ColorlibStepIcon(props: StepIconProps) {
const classes = useColorlibStepIconStyles();
const { active, completed } = props;
const icons: { [index: string]: React.ReactElement } = {
1: <SettingsIcon />,
2: <GroupAddIcon />,
3: <VideoLabelIcon />,
};
return (
<div
className={clsx(classes.root, {
[classes.active]: active,
[classes.completed]: completed,
})}
>
{icons[String(props.icon)]}
</div>
);
}
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
width: '100%',
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
},
button: {
marginRight: theme.spacing(1),
},
instructions: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
},
stepperContainer: {
width: '100%',
},
contentContainer: {
width: '50%',
display: 'flex',
justifyContent: 'center',
},
})
);
function getSteps() {
return ['Select campaign settings', 'Create an ad group', 'Create an ad'];
}
function getStepContent(step: number) {
switch (step) {
case 0:
return 'Select campaign settings...';
case 1:
return 'What is an ad group anyways?';
case 2:
return 'This is the bit I really care about!';
default:
return 'Unknown step';
}
}
const SignUp: React.FC = () => {
const classes = useStyles();
const [activeStep, setActiveStep] = React.useState(0);
const steps = getSteps();
const handleNext = () => {
setActiveStep((prevActiveStep) => prevActiveStep + 1);
};
const handleBack = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
const handleReset = () => {
setActiveStep(0);
};
return (
<div className={classes.root}>
<div className={classes.stepperContainer}>
<Stepper
dir="rtl"
alternativeLabel
activeStep={activeStep}
connector={<ColorlibConnector />}
>
{steps.map((label) => (
<Step key={label}>
<StepLabel StepIconComponent={ColorlibStepIcon}>
{label}
</StepLabel>
</Step>
))}
</Stepper>
</div>
<div className={classes.contentContainer}>
<div>
{activeStep === steps.length ? (
<div>
<Typography className={classes.instructions}>
All steps completed - you&apos;re finished
</Typography>
<Button onClick={handleReset} className={classes.button}>
Reset
</Button>
</div>
) : (
<div>
<Typography className={classes.instructions}>
{getStepContent(activeStep)}
</Typography>
<div>
<Button
disabled={activeStep === 0}
onClick={handleBack}
className={classes.button}
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={handleNext}
className={classes.button}
>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
)}
</div>
</div>
</div>
);
};
export default SignUp;

Hi I was fiddling around with the material UI sandbox,
here's the sandbox with working left to right
things I've change, were:
line 43 - initate step to 2
line 62 - handleNext, prevActiveStep - 1 (from +1)
line 67 - handleBack, prevActiveStep + 1 (from -1)
line 86 - setActiveStep(2) (from setActiveStep(0))
the Stepper component:
<Stepper activeStep={activeStep}>
{steps.map((label, index) => {
const stepProps: { completed?: boolean } = {
completed: activeStep < index // <<< this setting
};
const labelProps: { optional?: React.ReactNode } = {};
if (isStepOptional(index)) {
labelProps.optional = (
<Typography variant="caption">Optional</Typography>
);
}
if (isStepSkipped(index)) {
stepProps.completed = false;
}
return (
<Step key={label} {...stepProps}>
<StepLabel {...labelProps}>{label}</StepLabel>
</Step>
);
})}
</Stepper>
hope that helps

Related

How to highlight the selected item in the dropdown list of Picker in react native

I'm using the Picker component of react native and want to apply CSS like background color for the selected item in the dropdown list of Picker.
below is my code snippet:
<Text style={clStyle.schoolNameLabel}>School Board*</Text>
<Picker
mode="dropdown"
itemStyle={clStyle.schoolNamePickerStyle}
style={clStyle.schoolNamePickerStyle}
placeholder="Select"
selectedValue={values.board_id}
onValueChange={(value) => {
handleSchoolBoardChange(value, setFieldTouched, handleChange);
}}
enabled={schools && schools.length > 0 ? false : true}
>
<Picker.Item label="Select" color="#ccc" value="" />
{updateBoardDropdown()}
</Picker>
updateBoardDropdown = () => {
try {
const all_items =
this.props.metaData &&
this.props.metaData.boardResponse &&
this.props.metaData.boardResponse.length > 0 &&
this.props.metaData.boardResponse.map((_board: BoardResponse, i: number) => {
return (
<Select.Item
key={_board.id}
backgroundColor="yellow"
color="red"
label={_board.name}
value={_board.id.toString()}
/>
);
});
return all_items;
} catch (e) {
SentryException({
property: '🚀 ~ file: create-lead-screen.tsx ~ line 674 ~ CreateLead ~ e',
message: e as Error,
});
}
};
Please help Thanks
In updateBoardDropdown add check of values.board_id== _board.id like
<Select.Item
key={_board.id}
backgroundColor="yellow"
color="red"
label={_board.name}
value={_board.id.toString()}/>
to
<Select.Item
key={_board.id}
backgroundColor={values.board_id== _board.id ? "grey":"yellow"}
color="red"
label={_board.name}
value={_board.id.toString()}/>
you can use the dropdown from native-base and here is the code.
import { NativeBaseProvider, Select } from 'native-base';
import React, { memo } from 'react';
import { Text, View } from 'react-native';
import { RFValue } from 'react-native-responsive-fontsize';
import { hp, wp } from '../constants/scaling';
import { colors, fonts } from '../constants/theme';
import selectStyles from '../styles/selectStyles';
import { textStyles } from '../styles/textStyles';
const CustomDropDownInput = ({ label, placeholder, data, value, setValue }) => {
return (
<View style={{ alignSelf: 'center', height: hp(12) }}>
<NativeBaseProvider>
<Text
style={{
...textStyles.Label,
color: 'rgba(255,255,255,1)',
alignSelf: 'flex-start'
}}>
{label}
</Text>
<Select
useNativeDriver={true}
selectedValue={value}
minWidth={wp(96)}
accessibilityLabel={placeholder}
placeholder={placeholder}
placeholderTextColor={'rgba(255,255,255,1)'}
_item={selectStyles._item}
customDropdownIconProps={{
color: '#fff',
marginRight: wp(3)
}}
_selectedItem={selectStyles._selectedItem}
mt={1}
borderWidth={0}
borderRadius={0}
h={hp(7)}
backgroundColor={colors.inputBgColor}
fontFamily={fonts.Medium}
color={'#fff'}
fontSize={RFValue(14)}
borderBottomColor={'#fff'}
borderBottomWidth={1}
style={{
backgroundColor: '#0000',
borderWidth: 0
}}
onValueChange={itemValue => setValue(itemValue)}>
{data.map((item, index) => {
return (
<Select.Item
label={item.label}
value={item.value}
key={`${index}`}
/>
);
})}
</Select>
</NativeBaseProvider>
</View>
);
};
const compareProps = (prevProps, nextProps) => {
if (prevProps == nextProps) {
return false;
}
return true;
};
export default memo(CustomDropDownInput, compareProps);
here are the selectStyles.js file
import {Icon} from 'native-base';
import React from 'react';
import {Platform} from 'react-native';
import {RFValue} from 'react-native-responsive-fontsize';
import Entypo from 'react-native-vector-icons/Entypo';
import {wp} from '../constants/scaling';
import {colors, fonts} from '../constants/theme';
const selectStyles = {
_item: {
pt: Platform.OS === 'android' ? 2 : 2,
pb: Platform.OS === 'android' ? 2 : 2,
borderBottomWidth: 1,
justifyContent: 'center',
borderRadius: 5,
borderBottomColor: 'rgba(0,0,0,.1)',
_text: {
includeFontPadding: false,
textAlign: 'center',
fontFamily: fonts.Medium,
fontSize: RFValue(13),
color: colors.textPrimaryColor,
},
},
_selectedItem: {
bg: colors.primaryColor,
justifyContent: 'center',
_text: {
includeFontPadding: false,
textAlign: 'center',
fontSize: RFValue(13),
textAlignVertical: 'center',
fontFamily: fonts.Bold,
color: '#fff',
},
endIcon: (
<Icon as={Entypo} name={'check'} size={wp(5)} color={colors.white} />
),
},
};
export default selectStyles;

React.js Image Slider Back Button

I am fairly new to JavaScript and React, so I was hoping someone would be able to help me with something that seems fairly easy, I just don't fully grasp the concepts. I do not want to use ReactRouter, just manage it within the file.
I am creating a simple image slider using React and Material UI. However, I am having some difficulty with the functionality of the back button, preventing the app from crashing when going past the first image (into the negatives), and displaying the image number (ie: 3/5).
Here is my code:
import React, { useState } from "react";
import Button from "#material-ui/core/Button";
import MobileStepper from "#material-ui/core/MobileStepper";
import Paper from "#material-ui/core/Paper";
import KeyboardArrowRight from "#material-ui/icons/KeyboardArrowRight";
import Typography from "#material-ui/core/Typography";
import { useTheme } from "#material-ui/core/styles";
import KeyboardArrowLeft from "#material-ui/icons/KeyboardArrowLeft";
const myCollection = [
{
label: "First Picture",
imgPath:
"https://randompicturegenerator.com/img/dog-generator/g0ae914387a19ba58fc07fffe7f6952176159f445a3cd128c43ad59ae8b9baed35d8784cb3cdf4f4c16897571568d60c5_640.jpg",
},
{
label: "Second Picture",
imgPath:
"https://randompicturegenerator.com/img/dog-generator/g913a2b6f81253654df3b9e66abc189e6b966daf7a7a37b814b2336ab4459a832db90c8b923a5a8e28e63bb2fdd4496e1_640.jpg",
},
{
label: "Third Picture",
imgPath:
"https://randompicturegenerator.com/img/dog-generator/g5d33df79829d9cfbba39903471d1cd1bc2d6cdd243e222607e337d5575cb76ed10582deda8ed10bb0d1c3c43a6494f5a_640.jpg",
},
{
label: "Fourth Picture",
imgPath:
"https://randompicturegenerator.com/img/dog-generator/g446bd15f34f6fcb35272fa878493a90e9600b73e57f1f1dbca68ac22b9a1f780cb58c8a940d5727f930aa78d7b537b82_640.jpg",
},
{
label: "Fifth Picture",
imgPath:
"https://randompicturegenerator.com/img/dog-generator/g2efe8ff4951d16cc2d042d3714796204a975eb2c32fc5ee60d08956a729b7d5114b32cb0db6a5ba2e6d2948b8a6a0320_640.jpg",
},
];
const App = () => {
const CollectionSize = myCollection.length;
const theme = useTheme();
const [index, setIndex] = useState(0);
const goToNextPicture = () => {
setIndex((prevIndex) => prevIndex + 1);
};
const goToPrevPicture = () => {
setIndex((prevIndex) => prevIndex - 1);
};
return (
<>
<div style={{ marginLeft: "40%" }}>
<h2>Mans Best-Friend</h2>
<div style={{ maxWidth: 400, flexGrow: 1 }}>
<Paper
square
elevation={0}
style={{
height: 50,
display: "flex",
paddingLeft: theme.spacing(4),
backgroundColor: theme.palette.background.default,
alignItems: "center",
}}
>
<Typography>{myCollection[index].label}</Typography>
</Paper>
<img
src={myCollection[index].imgPath}
style={{
height: 255,
width: "100%",
maxWidth: 400,
display: "block",
overflow: "hidden",
}}
alt={myCollection[index].label}
/>
<MobileStepper
variant="text"
position="static"
index={index}
steps={CollectionSize}
nextButton={
<Button
size="small"
onClick={goToNextPicture}
disabled={index === CollectionSize - 1}
>
Next
{theme.direction !== "rtl" ? (
<KeyboardArrowRight />
) : (
<KeyboardArrowLeft />
)}
</Button>
}
/>
<MobileStepper
variant="text"
position="static"
index={index}
steps={CollectionSize}
backButton={
<Button
size="small"
onClick={goToPrevPicture}
disabled={index === CollectionSize + 1}
>
Back
{theme.direction !== "ltl" ? (
<KeyboardArrowRight />
) : (
<KeyboardArrowLeft />
)}
</Button>
}
/>
</div>
</div>
</>
);
};
export default App;
Error I am getting in the console:
const goToNextPicture = () => {
index<{your_array}.length-1?
setIndex((prevIndex) => prevIndex + 1):setIndex(0);
};
const goToPrevPicture = () => {
index>0
setIndex((prevIndex) => prevIndex - 1):setIndex(arr.length-1);
};
enter code here

Why is this MUI react data grid component rendered 4 times?

I have made a wrapper for MUI Data Grid Component as below:
Selection.tsx:
import * as React from 'react';
import { DataGrid, faIR, GridSelectionModel } from '#mui/x-data-grid';
import type {} from '#mui/x-data-grid/themeAugmentation';
import { createTheme, makeStyles, ThemeProvider } from '#mui/material/styles';
import { useRef, useState } from "react";
import { TablePagination } from "#mui/material";
import Stack from "#mui/material/Stack";
import DeleteIcon from '#mui/icons-material/Delete';
export default function ControlledSelectionServerPaginationGrid(props: any) {
const theme = createTheme(
{
breakpoints: {
values: {
xs: 300,
sm: 600,
md: 900,
lg: 1200,
xl: 1536
}
},
palette: {
primary: { main: '#1976d2' },
},
components: {
MuiDataGrid: {
styleOverrides: {
root: {
display: "flex",
flexDirection: "column-reverse",
borderRadius: '0',
paddingRight: '0px',
},
menu: {
direction: "rtl",
},
footerContainer: {
borderBottom: '1px solid #e0e0e0'
},
cell: {
textAlign: 'right',
'&:last-child': {
textAlign: 'center',
},
}
}
},
MuiTablePagination: {
styleOverrides: {
root: {
borderRadius: 'none',
direction: 'rtl',
},
displayedRows: {
margin: 0,
direction: 'ltr',
},
actions: {
direction: "ltr"
}
}
},
MuiToolbar: {
styleOverrides: {
root: {
display: 'flex',
flexDirection: 'row-reverse',
}
}
},
MuiTableCell: {
styleOverrides: {
root: {
border: 'none'
}
}
}
},
},
faIR,
);
const [page, setPage] = useState<number>(0);
const { packages, loading } = props.FetchData(page);
const [selected, setSelected] = useState<any>([]);
const [rowsPerPage, setRowsPerPage] = useState<number>(15);
const handleChangePage = (event: React.MouseEvent<HTMLButtonElement> | null, pageNumber: number) => {
setPage(pageNumber);
};
const handleChangeRowsPerPage = (
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
setRowsPerPage(parseInt(event.target.value));
setPage(0);
};
const CustomFooter = () => {
return (
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
sx={{
borderBottom: '1px solid #e0e0e0'
}}
>
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
sx={{
marginRight: '20px',
}}
>
{
selected && selected.length > 0 && (
<DeleteIcon sx={{color: "gray", cursor: 'pointer'}} onClick={() => alert('deleted them all')} />
)
}
</Stack>
<CustomPagination/>
</Stack>
)
}
const CustomPagination = () => {
return (
<TablePagination
count={packages?.meta?.last_page}
page={page}
onPageChange={handleChangePage}
rowsPerPage={rowsPerPage}
labelRowsPerPage={false}
onRowsPerPageChange={handleChangeRowsPerPage}
rowsPerPageOptions={[15, 30, 45, 60]}
/>
)
}
return (
<div style={{ height: '790px', width: '100%' }}>
<ThemeProvider theme={theme}>
{
loading ? (
<div>
loading
</div>
) : (
<DataGrid
rows={packages.data}
getRowId={(row) => row.uuid}
columns={props.columns}
pagination
components={{Footer: CustomFooter}}
checkboxSelection
disableSelectionOnClick
hideFooterSelectedRowCount
loading={loading}
onSelectionModelChange={(ids) => {
setSelected(ids);
}}
/>
)
}
</ThemeProvider>
</div>
);
}
I have the below hook as well which I'm passing to the above component as FetchData:
usePackages.tsx:
export default function usePackages(page: number) {
const [loading, setLoading] = useState<boolean>(false);
const [packages, setPackages] = useState<any>({
data: [],
meta: {},
links: {}
});
useEffect(() => {
setLoading(true);
getListOfAllPackages(page)
.then(data => {
setPackages({
...data.data,
});
setLoading(false);
})
.catch(error => {
setLoading(false);
});
}, [page]);
return { packages, loading };
Now the way I'm using the above components is just like below:
Packages.tsx:
import usePackages from "../../../hooks/Package/usePackages";
const configValues = (params: any) => {
return `${params.row.config.hdd}, ${params.row.config.cpu}, ${params.row.config.ram}`
}
const columns = [
{
field: "product_name",
headerName: "Product Name",
width: '200',
flex: 1
},
{
field: "name",
headerName: "Name",
width: '400',
flex: 1
},
{
field: "config",
headerName: "Config",
width: '150',
flex: 1,
valueGetter: configValues,
},
{
field: "description",
headerName: "Description",
width: '350',
flex: 1
},
{
field: 'actions',
type: 'Actions',
width: '100',
flex: 1,
},
]
return (
<div style={{ display: 'flex', height: '100%', marginTop: '64px'}}>
<div style={{ flexGrow: 1 }}>
<ControlledSelectionServerPaginationGrid columns={columns} FetchData={usePackages}/>
</div>
</div>
)
Now my Selection.tsx renders nearly 4 times and making the other components open with delay like modals and the Side Nav, I tried to console log everything and found out it's because of my passed hook props.FetchData that Selection.tsx re-renders itself, hence checking usePackages.tsx and finding out nothing is wrong. I've used such pattern previously and didn't confront such related issues.
I'm not sure the exact reason for these rerenders though. But I saw some potential things that may affect it.
First, I think it's better to put some variables like theme outside of your component body. Because on each rerender, this variable recreates.
Then, make sure to memorize some of the functions that you are using inside your component body like CustomPagination and CustomFooter with the useMemo hook. Because these are recreating with every rerender that happens, too.
After that, based on my experience, it's better to not pass functions through props. Because React can not compare functions for previous props and next props for each rerender. It's better to abstract functions and import them into the component file that you need.
Here, you can import usePackages directly from Selection.tsx instead of using it from props.
That's the idea behind hooks.

Material UI's Stepper StepLabel Icon issue with undefined

just having issues with trying to implememnt a custom Step Label Icon within the nodes of the Stepper Component provided by Material UI. I am trying to implement an icon within each circle, as seen here from Material UI's demo
however, am coming across an error
Please see my code below. Thanks!
import React from 'react';
import { Typography } from '#material-ui/core';
import { withStyles, styles } from '#material-ui/core/styles';
const styles = theme => ({
checklistHeader: {
fontWeight: 'bold',
marginTop: '80px',
color: 'white'
},
connectorIcon: {
color: theme.palette.text.secondary
},
active: {
backgroundColor: 'green'
}
});
const steps = ['Select campaign settings', 'Select campaign settings', 'Select campaign settings'];
const ColorlibStepIconRoot = styled('div')(({ theme, ownerState }) => ({
backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[700] : '#ccc',
zIndex: 1,
color: '#fff',
width: 50,
height: 50,
display: 'flex',
borderRadius: '50%',
justifyContent: 'center',
alignItems: 'center',
...(ownerState.active && {
backgroundImage:
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)',
}),
...(ownerState.completed && {
backgroundImage:
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
}),
}));
const ColorlibStepIcon = ({
icon, active, completed, className
}) => {
const icons = {
1: <Icon style={{ color: 'red' }}>create_outline</Icon>,
2: <Icon style={{ color: 'red' }}>star</Icon>,
3: <Icon style={{ color: 'red' }}>people</Icon>,
};
return (
<ColorlibStepIconRoot ownerState={{ completed, active }} className={className}>
{icons[String(icon)]}
</ColorlibStepIconRoot>
);
};
const Stepper = ({ classes }) => {
return (
<React.Fragment>
<Typography variant="h6" align="center" gutterBottom className={classes.checklistHeader}>Please complete the following criterion</Typography>
<Stepper alternativeLabel activeStep={2} style={{ background: 'none' }}>
{steps.map(label => (
<Step key={label}>
<StepLabel StepIconComponent={ColorlibStepIcon}>{label}</StepLabel>
</Step>
))}
</Stepper>
</React.Fragment>
);
};
Stepper.defaultProps = {
};
Stepper.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles, { withTheme: true })(Stepper);
It seems like the styled component is undefined. Is there any way that I can bypass this
The styled() function is only available in v5. You either need to upgrade to MUI v5 using this installation guide or use the older API in v4, (The equivalent is withStyles):
V5
import { styled } from '#mui/material/styles';
const StyledComponent = styled(Component)({
// your styles
});
V4
import { withStyles, styles } from '#material-ui/core/styles';
const StyledComponent = withStyles({
// your styles
})(Component);
You can see the v4 docs here and the v5 docs here.

REACT : Error: Rendered fewer hooks than expected. This may be caused by an

i have a problem i have the famous react error : Uncaught (in promise) Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
But i'm looking for the issue but i didnt find because i think my hooks order are well but its not the case.... Do you have an idea where is the mistake?
Thanks
import { useNavigate, Link, useParams } from 'react-router-dom';
import { useEffect, useState, useCallback, useMemo } from 'react';
import { Formik } from 'formik';
import { useIntl, FormattedMessage } from 'react-intl';
import { useQuery, gql, useMutation } from '#apollo/client';
import { sub } from 'date-fns';
import {
Text,
Button,
TextField,
DateField,
Banner,
Loader,
Icon,
} from '#customer-portal/components';
import { StandardField } from '../../components/FormikFields';
import { useError } from '../../hooks/useError';
import { toISODate, toStartOfDay } from '../../utils/date';
import { useFilteredContracts } from '../../components/ContractsFilter';
export function MeterReadingToEnterForm() {
const [date, setDate] = useState();
const [forceResult, setForceResult] = useState(false);
const [successBanner, setSuccessBanner] = useState(false);
let navigate = useNavigate();
let { serial } = useParams();
const intl = useIntl();
const contract = useFilteredContracts();
const initialValues = {};
const getError = useError(error);
useEffect(() => {
return () => {
navigate('/meter-reading');
};
}, [contract.id]);
const {
loading,
data: queryData,
error,
refetch,
} = useQuery(QUERY, {
notifyOnNetworkStatusChange: true,
fetchPolicy: 'network-only',
variables: {
date: toStartOfDay(Date.now()),
contractId: contract.id,
},
});
const [enterMeterReading, { loading: enterLoading, error: enterError }] =
useMutation(MUTATION_QUERY, {
onCompleted: () => setSuccessBanner(true),
});
useEffect(() => {
refetch({ variables: { date: toStartOfDay(date) } });
}, [date]);
if (error) {
return (
<Banner data-test="BannerNoContracts" type="error" iconName="Alert">
<Text>{getError()}</Text>
</Banner>
);
}
if (enterError) {
return (
<Banner type="error" iconName="Alert">
<Text>{getError()}</Text>
</Banner>
);
}
const formattedValues = (valeur) => {
let array = Object.keys(valeur).map((key) => ({
id: key,
result: valeur[key],
}));
return array;
};
const handle = useCallback((values) => {
mutationQuery({
variables: {
data: {
serial: serial,
results: formattedValues(values),
},
Id: id,
},
refetchQueries: ['newQuery'],
});
});
return (
<div sx={{ display: 'flex', flexDirection: 'column', width: '100%', p: 2 }}>
<div sx={{ display: 'flex', pt: 6, width: '100%' }}>
<Icon
sx={{ display: 'flex', alignItems: 'center', m: 3 }}
color="primary"
name="Counter"
size="large"
/>
<Text weight="bold" sx={{ display: 'flex', alignItems: 'center' }}>
<FormattedMessage
defaultMessage="N° {number}"
values={{
number: `${serial}`,
}}
/>
</Text>
</div>
{successBanner && (
<div sx={{ mb: 4 }}>
<Banner
iconName="SuccessOutline"
type="success"
>
<Text>
<FormattedMessage
defaultMessage="Done"
/>
</Text>
</Banner>
</div>
)}
<div sx={{ display: 'flex', justifyContent: 'flex-start', py: 2 }}>
<DateField
name="datefoield"
value={date || Date.now()}
onChange={(date) => setDate(date)}
required={true}
label={intl.formatMessage({
defaultMessage: 'Date du relevé',
})}
defaultSelected={date}
disabledDays={[
{
before: sub(new Date(toISODate(Date.now())), {
days: 60,
}),
},
{
after: new Date(Date.now()),
},
]}
/>
</div>
{enterLoading && <Loader type="radiance" overlay={true} />}
{loading ? (
<Loader sx={{ mx: [0, 11] }} />
) : (
<Formik
onSubmit={handleEnterMeterReading}
initialValues={initialValues}
>
{({ values, handleSubmit }) => {
const query = useMemo(() => {
return queryDatafind(
(serialNumber) => serialNumber === serial
);
}, [queryData]);
return (
<form
noValidate={true}
onSubmit={handleSubmit}
>
<div
sx={{
display: 'flex',
flexWrap: 'wrap',
width: '100%',
py: 2,
}}
>
{query.map((value) => (
<div sx={{ mr: 4 }}>
<StandardField
as={TextField}
id={id}
label={label}
required
name={id}
size="standard"
type="number"
variant="standard"
/>
</div>
))}
</div>
<div sx={{ display: 'flex', py: 6 }}>
<Link to="/">
<Button
sx={{ my: 4, mr: 4 }}
startIcon="ErrorOutline"
size="standard"
variant="outlined"
>
{intl.formatMessage({
defaultMessage: 'Annuler',
})}
</Button>
</Link>
<Button
sx={{ whiteSpace: 'nowrap', my: 4 }}
startIcon="SuccessOutline"
size="standard"
type="submit"
disabled={loading}
>
{loading
? intl.formatMessage({
defaultMessage: 'loading',
})
: intl.formatMessage({
defaultMessage: 'Validate',
})}
</Button>
</div>
</form>
);
}}
</Formik>
)}
</div>
);
}
-- GRAPHQL REQUEST---
The issue is that you call useCallback after some if conditionals that may return prematurely. You could either remove the useCallback call (just set handle to a new function closure each rendering), or move the useCallback call to above the if ... returns.

Categories

Resources