I am trying to pass properties to component and in which to upload file for right user.
Here is sandbox example:
https://codesandbox.io/s/divine-moon-mj5w6n?file=/src/Main.tsx
As you can see I have dummy data:
export default [
{
id: "1",
name: "John Doe",
avatar: "app.png"
},
{
id: "2",
name: "Anna Doe",
avatar: "pic.png"
},
{
id: "3",
name: "Michael Doe",
avatar: "mypic.png"
}
];
In my Main component I load CustomDataGrid component in which I am passing in one of its columns UploadAvatar component:
{
field: "avatar",
headerName: "Аватар",
headerClassName: "home-header",
editable: false,
align: "center",
headerAlign: "center",
renderCell: (params) => (
<UploadAvatar
avatar={params.value}
name={params.row.name}
userId={params.row.id}
/>
)
},
The problem is the following as you can test:
1. Click avatar of the third Person Michael Doe
The console.log from this row:
onClick={() => {
console.log(userId);
document.getElementById("avatar-input")!.click();
}}
Gives correct passed parameters:
mypic.png Michael Doe 3
2. After choosing file, the console.log() from uploadAvatar function:
const uploadAvatar = (e: any) => {
const file = e.target.files[0];
if (!file) return;
console.log(avatar, name, userId);
};
Gives parameters:
app.png John Doe 1
Which are parameters the first user of the list, not the passed one. This is very strange for me and why I am having exactly the parameters I want in first console.log but suddenly when go in my uploadAvatar function I have different? How can I fix this behavior?
EDIT 1:
I am pasting Upload Component as you wanted:
import React from "react";
import Avatar from "#mui/material/Avatar";
const UploadAvatar = ({
avatar,
name,
userId
}: {
avatar: string;
name: string;
userId: string;
}) => {
const uploadAvatar = (e: any) => {
const file = e.target.files[0];
if (!file) return;
console.log(avatar, name, userId);
};
return (
<div>
{avatar ? (
<Avatar
role="button"
tabIndex={0}
sx={{
marginLeft: "-10px",
marginRight: "10px",
"&:hover": { cursor: "pointer" }
}}
alt="avatar"
//src={`${shareServer}/avatars/${avatar}`}
onClick={() => {
console.log(avatar, name, userId);
document.getElementById("avatar-input")!.click();
}}
></Avatar>
) : (
<Avatar
role="button"
tabIndex={0}
sx={{
marginLeft: "-10px",
marginRight: "10px",
"&:hover": { cursor: "pointer" }
}}
alt="avatar"
src=""
onClick={() => {
document.getElementById("avatar-input")!.click();
}}
>
{name
? name
.split(/\s/)
.reduce(
(response: any, word: any) => (response += word.slice(0, 1)),
""
)
: ""}
</Avatar>
)}
<input
id="avatar-input"
type="file"
onChange={uploadAvatar}
style={{ display: "none", position: "absolute" }}
accept="image/*"
/>
</div>
);
};
export default UploadAvatar;
Set unique id for file input:
<input
id={name}
type="file"
onChange={uploadAvatar}
style={{ display: "none", position: "absolute" }}
accept="image/*"
/>
<Avatar
role="button"
tabIndex={0}
sx={{
marginLeft: "-10px",
marginRight: "10px",
"&:hover": { cursor: "pointer" }
}}
alt="avatar"
//src={`${shareServer}/avatars/${avatar}`}
onClick={() => {
document.getElementById(name).click();
}}
/>
And use ref for access DOM elements.
NOT:
document.getElementById(name).click();
With refs:
ref = useRef()
//...
<input
ref={ref}
id={name}
type="file"
onChange={uploadAvatar}
style={{ display: "none", position: "absolute" }}
accept="image/*"
/>
//...
ref.current.click()
With ref wouldn't be a problem with not unique id.
https://codesandbox.io/s/gallant-williamson-eqx82b?file=/src/component/UploadAvatar.tsx
Related
I'm struggling with Formik when using custom component for <Field> and my own onChange for this input.
The problem is that whenever I resetForm() (it's now set inside AutoSubmitHandler just for tests), the values are set well, it means that I get { q: ''} after each reset, but the value inside input itself stays filled.
Reproduction steps:
I type something in the input - "foo"
I get console.logged the values: { q: "foo" }
The resetForm() triggers and I get { q: ''}
But in the input I can still see "foo"
It looks like this:
const CustomDarkTextField = styled(TextField)<TextFieldProps>(() => ({
width: '50%',
marginBottom: '10px',
bgcolor: '#5C677D',
'& .MuiInputBase-root': {
bgcolor: '#5C677D',
},
'& .MuiInputBase-input': {
color: 'white',
},
'& .MuiFormLabel-root': {
color: 'white',
fontWeight: 600,
},
'& .MuiFormLabel-root.Mui-focused': {
color: 'white',
fontWeight: 600,
},
'& .MuiFormHelperText-root.Mui-error': {
color: '#F77F00',
},
'& .MuiOutlinedInput-root.Mui-focused': {
'& > fieldset': { border: '0' },
},
'& .MuiOutlinedInput-root.Mui-error': {
'& > fieldset': { borderColor: '#F77F00' },
},
'& .MuiTypography-root': {
color: 'white',
fontWeight: 600,
},
}));
export const FilterCustomDarkTextField = ({ field, label, onChange, setFieldValue }: FilterTextFieldProps) => {
return (<CustomDarkTextField name={field?.name} label={label} onChange={e => onChange(e, setFieldValue)} />);
};
and my filter form component:
return (
<div>
<Formik
initialValues={defaultFilterValues}
onSubmit={(values) => {
handleSubmit(values);
}}
enableReinitialize={true}
>
{({ setFieldValue, values }) => (
<Form className='form flex flex-col gap-4 mx-auto my-8 w-full items-center'>
<h2 className="self-start">Data</h2>
{Object.keys(filterFields).map(field => (
<>
<Field
key={field}
{...filterFields[field as keyof typeof filterFields]}
setFieldValue={setFieldValue}
/>
</>
))}
<AutoSubmitHandler />
</Form>
)}
</Formik>
</div>
);
AutoSubmit:
const AutoSubmitHandler = () => {
const { values, resetForm } = useFormikContext();
useEffect(() => {
console.log(values);
resetForm();
}, [values]);
return null;
};
fields:
const FILTER_FIELDS = {
football: {
q: {
name: 'q',
label: 'name/surname',
component: FilterCustomDarkTextField,
onChange: debouncedHandleChange,
},
},
};
onChange method:
export const debouncedHandleChange = _.debounce((e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, setFieldValue) => {
const { name, value } = e.target;
setFieldValue(name, value);
}, 1500);
The code below I have provided , one example opens the dialog. And the dialog has add functionality which is the addEmail where in you can add multiple fields which is inside the dialog.
I wanna know is that when I close the dialog using the onClick={handleClose} it should reset the dialog, fields that I added should not show after I close since I did not save it.
So when I click cancel it should reset the state.
Thanks for any idea.
for example here I added fields when I close this and open again these field should not show cause it should reset when I close.
#interface.ts
export type EditPropertiesProps = {
open: boolean;
handleClose: () => void;
selectedRow: IRegional
};
#Code snippet - main page --- this calls and opens the dialog
const handleClose = () => {
console.log('here')
setOpen(false);
};
<EditProperties open={open} handleClose={handleClose} selectedRow={selectedRow} />
#EditProperties ts code
export const RegionalListData: IRegionalList[] = [
{
id: 4,
name: "Associate Director of Construction Ops",
column: "associateDirectorofConstructionOps",
emails: [
{
emailAddress: "associateDir#gmail.com",
firstName: "Associate",
lastName: "Director",
id: Math.floor(Math.random() * 999),
fetching: false,
},
],
},
{
id: 5,
name: "CAM Manager",
column: "camManager",
emails: [
{
emailAddress: "associateDir#gmail.com",
firstName: "Associate",
lastName: "Director",
id: Math.floor(Math.random() * 999),
fetching: false,
},
],
},
{
id: 6,
name: "CAO-Chief Administrative Officer",
column: "caoChiefAdministrativeOfficer",
emails: [
{
emailAddress: "associateDir#gmail.com",
firstName: "Associate",
lastName: "Director",
id: Math.floor(Math.random() * 999),
fetching: false,
},
],
},
];
type InitialReqPaylod = {
accountId: number;
regionalRoleUserDto: IRegional;
};
type IData = {
regionName: string;
marketName: string;
subRegionName: string;
};
type IEmail = {
emailAddress: string;
firstName: string;
id: number;
lastName: string;
};
const EditProperties: FC<EditPropertiesProps> = ({
open,
handleClose,
selectedRow,
}) => {
const dispatch = useAppDispatch();
const [isEmailOpen, setOpenEmail] = useState(false);
const [fetching, setFetching] = useState(false);
const [RegionalList, setRegionalList] = useState<IRegionalList[]>(
RegionalListData
);
const [data, setData] = useState<IData>({
regionName: "",
marketName: "",
subRegionName: "",
});
const [regionalId, setRegionalId] = useState<number | null>(null);
const [emailCurrentIndex, setEmailCurrentIndex] = useState<number | null>(
null
);
const [selectedEmailId, setSelectedEmailId] = useState<number | null>(null);
const { isSuccess } = useAppSelector((state) => state.yardUser);
const { isSaveSuccess } = useAppSelector((state) => state.region);
const email = useAppSelector((state) => state.yardUser);
const [emailOptions, setEmailOptions] = useState<IEmail[]>([]);
const emailList = email.data ? email.data.data : [];
useEffect(() => {
if (selectedRow) {
setData({
regionName: selectedRow["regionName"],
marketName: selectedRow["marketName"],
subRegionName: selectedRow["subRegionName"],
});
let regional = [...RegionalList];
for (const k in selectedRow) {
regional.map((prop: IRegionalList) => {
if (prop.column === k) {
prop.emails = selectedRow[k] ? selectedRow[k] : [];
}
});
}
setRegionalList(regional);
}
}, [selectedRow]);
const [maxWidth, setMaxWidth] = React.useState<DialogProps["maxWidth"]>("md");
const fetchEmailResult = React.useMemo(
() =>
throttle(
(event: any, callback: (results: IEmail[]) => void) => {
const payload: IYardUserRequestPayload | InitialReqPaylod = {
accountId: 1,
searchString: event.target.value,
};
fetch(
`https://jsonplaceholder.typicode.com/users?email=${event.target.value}`
)
.then((res) => res.json())
.then((res) => res.data ? callback(res.data.slice(0, 10)) : callback([]))
},
200
),
[]
);
const emailOnChange = (event: any, regionalId: number, index: number, emailId: number) => {
setRegionalId(regionalId);
setEmailCurrentIndex(index);
setSelectedEmailId(emailId);
fetchEmailResult(event,(results: IEmail[]) => {
console.log('results' , results)
if (results.length) setEmailOptions(results);
});
};
useEffect(() => {
if (isSaveSuccess) {
handleClose();
}
}, [isSaveSuccess]);
useEffect(() => {
if (isSuccess) {
setFetching(false);
}
}, [isSuccess]);
const addEmail = (id: number) => {
setRegionalList((list) =>
list.map((item) => {
if (item.id === id) {
return {
...item,
emails: [
...item.emails,
{
emailAddress: "",
firstName: "",
lastName: "",
id: Math.floor(Math.random() * 999),
fetching: false,
},
],
};
}
return item;
})
);
};
const deleteEmail = (email: IEmail, regionId: number) => {
const regionalListCopy = [...RegionalList].map((prop: IRegionalList) => {
if (prop.id === regionId) {
return {
...prop,
emails: prop.emails.filter((prop) => prop.id !== email.id),
};
}
return { ...prop };
});
setRegionalList(regionalListCopy);
};
const setOnChangeOption = (email) => {
setSelectedEmailId(null);
setRegionalList((list) =>
list.map((item) => {
if (item.id === regionalId) {
return {
...item,
emails: [
...item.emails.map((prop) => {
return {
...prop,
...email,
};
}),
],
};
}
return item;
})
);
};
const EmailItem = ({ email, mIndex, prop }) => (
<>
<div style={{ display: "block" }} key={email.id}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: 15
}}
>
<Autocomplete
options={emailOptions}
getOptionLabel={(option: IEmail) => option.emailAddress}
onInputChange={($event) => emailOnChange($event, prop.id, mIndex, email.id)}
onChange={($event, value) => setOnChangeOption(value)}
fullWidth
open={email.id === selectedEmailId}
renderInput={(params) => (
<TextField size="small" {...params} variant="standard" />
)}
renderOption={(props, option) => {
return (
<Box component="li" {...props}>
{option.emailAddress}
</Box>
);
}}
/>
<DeleteIcon
style={{ color: "red", cursor: "pointer" }}
onClick={() => deleteEmail(email, prop.id)}
/>
</div>
<div
style={{
fontSize: ".8em",
display: "flex",
justifyContent: "space-between",
}}
>
<span style={{ paddingTop: 5 }}>
Email : {email.emailAddress}
Full Name: {email.firstName} {email.lastName}
</span>
{/* <span style={{ paddingRight : 40 }}>{fetching ? "Fetching...." : null}</span> */}
</div>
</div>
</>
);
return (
<Dialog
maxWidth={maxWidth}
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Edit</DialogTitle>
<DialogContent>
<Card sx={{ minWidth: 275 }} style={{ padding: 20 }}>
<div>
<span>Sub-Region (Sub-Division)</span>
<Divider style={{ marginTop: 10 }} />
<FormControl sx={{ mt: 2, minWidth: 720 }}>
<TextField
label="Region (Division)"
variant="filled"
value={data.regionName}
/>
</FormControl>
</div>
<div style={{ marginTop: 10 }}>
<span>Sub-Region (Sub-Division)</span>
<Divider style={{ marginTop: 10 }} />
<FormControl sx={{ mt: 2, minWidth: 720 }}>
<TextField
label="Sub-Region (Sub-Division)"
variant="filled"
value={data.subRegionName}
/>
</FormControl>
</div>
<div style={{ marginTop: 10 }}>
<span>Market</span>
<Divider style={{ marginTop: 10 }} />
<FormControl sx={{ mt: 2, minWidth: 720 }}>
<TextField
label="Market"
variant="filled"
value={data.marketName}
/>
</FormControl>
</div>
</Card>
{RegionalList.map((prop: IRegionalList, index: number) => (
<Card
sx={{ minWidth: 275 }}
style={{ overflow: "visible", padding: 20, marginTop: 20 }}
key={prop.id}
>
<div style={{ display: "flex", alignItems: "center" }}>
{prop.name}*{" "}
<AddIcon
style={{ marginLeft: 5, cursor: "pointer" }}
onClick={() => addEmail(prop.id)}
/>
</div>
<Divider style={{ marginTop: 10 }} />
{prop.emails.map((email: IEmail, mIndex: number) => (
<EmailItem
key={email.id}
prop={prop}
email={email}
mIndex={mIndex}
/>
))}
</Card>
))}
</DialogContent>
<DialogActions
style={{ marginTop: "20px", marginRight: "20px", marginBottom: "20px" }}
>
<Button onClick={handleClose}>Cancel</Button>
<Button variant="contained" onClick={() => saveChanges()} autoFocus>
Save Changes
</Button>
</DialogActions>
</Dialog>
);
};
export default EditProperties;
You need just reset all used states as values of form when clicking handleClose, my suggestion would be to use just one object state for form values.
Example:
const onClose = () => {
handleClose();
setRegionalList(RegionalListData);
}
return (
<Dialog
maxWidth={maxWidth}
open={open}
onClose={onClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
I'm trying to fetch data using api and show the data in my datagrid. but initially the data is not loading. though I can get the data, and I don't know why it doesn't show in the datagrid. What I'm actually doing is, I have a common function for datagrid along with search option in one file. I'm getting the getting the data in another file. Now everytime I make a change and save the file, it shows the data, but when I refresh the page the data is not showing again. kindly help me figure out. Thanks in advance
this the code where I'm getting the data
const columns = [
{ field: 'role', headerName: 'Role', },
{ field: 'counterParty', headerName: 'Counter Party', },
{ field: 'tokens', headerName: 'Token', },
{ field: 'date', headerName: 'Date', },
{ field: 'comment', headerName: 'Comments', }
];
export default function TransactionCard(props) {
const [TransactionData, setTransactionData] = useState([]);
const [loading, setLoading] = useState(true);
// setLoading(loading, true);
useEffect(() => {
axios.get('http://localhost:8006/api/v2/user/transactions').then(function (res) {
try {
var result = res.data;
// console.log(result.data.data)
setTransactionData(result.data.data)
// setLoading(false);
}
catch (error) {
console.log(error)
}
})
}, [])
console.log(TransactionData)
return (
<>
<Card {...props}>
<CardContent>
<DataGridHelper title='Transaction' rows={TransactionData} columns={columns} />
</CardContent>
</Card>
</>
);
}
This is Where I'm getting the data
const DataGridHelper = ({ rows, columns, title }) => {
const [platform, setPlatform] = useState([]);
const [searchText, setSearchText] = useState('');
const [Rows, setRows] = useState([]);
useEffect(() => {
setPlatform(rows);
setRows(rows);
}, []);
console.log(Rows);
function escapeRegExp(value) {
return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}
const requestSearch = (searchValue) => {
const searchRegex = new RegExp(escapeRegExp(searchValue), 'i');
const filteredRows = platform.filter((row) => {
return Object.keys(row).some((field) => {
return searchRegex.test(row[field].toString());
});
});
setRows(filteredRows);
};
console.log(rows)
return (
<>
<Stack direction="row" alignItems="center" justifyContent='space-between' gap={1}>
<Stack direction="row" alignItems="center" gap={1}>
<RuleIcon sx={{ color: "text.secondary" }} />
<Typography sx={{ fontSize: 16, fontWeight: 'bold' }} color="text.secondary" gutterBottom>
{title}
</Typography></Stack>
<Box sx={{ flexDirection: 'column' }}>
<TextField
variant="outlined"
size='small'
value={searchText}
onChange={(e) => { setSearchText(e.target.value); requestSearch(e.target.value) }}
placeholder="Search..."
InputProps={{
startAdornment: <SearchIcon fontSize="small" color="action" />,
endAdornment: (
<IconButton
title="Clear"
aria-label="Clear"
size="small"
style={{ visibility: searchText ? 'visible' : 'hidden', borderRadius: "57%", paddingRight: "1px", margin: "0", fontSize: "1.25rem" }}
onClick={(e) => { setSearchText(''); setRows(platform) }}
>
<ClearIcon fontSize="small" color="action" />
</IconButton>
),
}}
sx={{
width: { xs: 1, sm: 'auto' }, m: (theme) => theme.spacing(1, 0.5, 1.5),
'& .MuiSvgIcon-root': {
mr: 0.5,
},
'& .MuiInput-underline:before': {
borderBottom: 1,
borderColor: 'divider',
},
}}
/>
</Box>
</Stack>
<div style={{ height: 250, width: '100%' }}>
<DataGrid
disableColumnMenu
rows={Rows}
columns={columns}
pageSize={10}
rowsPerPageOptions={[10]}
/>
</div>
</>
);
}
export default DataGridHelper
this is the data I'm getting initially
first of all, your code is so dirty :), i cant test your code cause its have your rest API and you don't name consoles, i just understand which one is result of axios, but i think your mistake is on useEffect depandecies in DataGridHelper you must write rows in it, and second maybe in useEffect when you get data in rest API you might write if condition for try in axios tell that if(!!result.data.data) then do that i think it must your problem
useEffect(() => {
setPlatform(rows);
setRows(rows);
}, [rows]);
and :
useEffect(() => {
axios.get('http://localhost:8006/api/v2/user/transactions').then(function (res) {
try {
if(!!res.data){
var result = res.data;
// console.log(result.data.data)
setTransactionData(result.data.data)
// setLoading(false);
}
}
catch (error) {
console.log(error)
}
})
}, [])
I have a multi-step form using kendo and basically what i want to do is to pass the values from the function Charging to one of the steps so i can use the user inputs in a fetch API GET request. Normally i would have done it with props, but the thing here is that i am passing data from a function. I tried useContext but i can't make it work and i went through the kendo form code many times in order to grasp its methodology but i still can't pass the values. I am getting the values from onStepSubmit() handler and i can get them outside this callback with useState, but even then i can't pass them.
Here is the code for the main function where i get the values
import * as React from "react";
import "./Main.css";
import { Form, FormElement } from "#progress/kendo-react-form";
import { Button } from "#progress/kendo-react-buttons";
import { Stepper } from "#progress/kendo-react-layout";
import { SelectVehicle } from "./components/SelectVehicle";
import { PaymentMethod } from "./components/PaymentMethod";
import chargeIcon from "../../../../img/svg-6.svg";
import { ChargingStep } from "./components/ChargingStep";
import { Payment } from "./components/Payment";
import axios from "axios";
import { AuthContext } from "../../../../shared/context/auth-context";
import Notification from "../../Vehicles/Vehicles1/components/Notification";
const stepPages = [SelectVehicle, PaymentMethod, ChargingStep, Payment];
export const Charging = () => {
const [step, setStep] = React.useState(0);
const [formState, setFormState] = React.useState({});
const [steps, setSteps] = React.useState([
{ label: "Select Vehicle", isValid: undefined },
{ label: "Method", isValid: undefined },
{ label: "Charging", isValid: undefined },
{ label: "Payment", isValid: undefined },
]);
const auth = React.useContext(AuthContext);
const [vehicleId, setVehicleId] = React.useState(false);
const [notify, setNotify] = React.useState({
isOpen: false,
message: "",
type: "",
});
const lastStepIndex = steps.length - 1;
const isLastStep = lastStepIndex === step;
const isPreviousStepsValid =
steps
.slice(0, step)
.findIndex((currentStep) => currentStep.isValid === false) === -1;
const onStepSubmit = React.useCallback(
//add fetch vehicle data based on ID
(event) => {
const { isValid, values } = event;
axios
.get(process.env.REACT_APP_BACKEND_URL + `/cars/user/${auth.userId}`)
.then((response) => {
for (var i = 0; i < response.data.vehicles.length; i++) {
if (values.vehicleID == response.data.vehicles[i]._id) {
setVehicleId(true);
return;
} else {
setVehicleId(false);
return;
}
}
});
const currentSteps = steps.map((currentStep, index) => ({
...currentStep,
isValid: index === step ? isValid : currentStep.isValid,
}));
setSteps(currentSteps);
setStep(() => Math.min(step + 1, lastStepIndex));
setFormState(values);
if (isLastStep && isPreviousStepsValid && isValid && vehicleId) {
// Send to api the data
//alert(JSON.stringify(values));
setNotify({
isOpen: true,
message: "Submitted Successfully",
type: "success",
});
} else if (isLastStep && isPreviousStepsValid && isValid && !vehicleId) {
setNotify({
isOpen: true,
message: "Wrong vehicle ID input",
type: "error",
});
}
},
[
step,
steps,
setSteps,
setStep,
setFormState,
lastStepIndex,
isLastStep,
isPreviousStepsValid,
]
);
const onPrevClick = React.useCallback(
(event) => {
event.preventDefault();
setStep(() => Math.max(step - 1, 0));
},
[step, setStep]
);
return (
<div>
<div className="vehicle__title">
<div className="main__title">
<img src={chargeIcon} alt="charging" />
<div className="main__greeting">
<h1>Charging Simulator</h1>
<p>Simulate a Charge</p>
</div>
</div>
</div>
<div className="wrapper__simulator">
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
}}
>
<Stepper value={step} items={steps} />
<Form
initialValues={formState}
onSubmitClick={onStepSubmit}
render={(formRenderProps) => (
<div style={{ alignSelf: "center" }}>
<FormElement style={{ width: 480 }}>
{stepPages[step]}
<span
style={{ marginTop: "40px" }}
className={"k-form-separator"}
/>
<div
style={{
justifyContent: "space-between",
alignContent: "center",
}}
className={"k-form-buttons k-buttons-end"}
>
<span style={{ alignSelf: "center" }}>
Step {step + 1} of 4
</span>
<div>
{step !== 0 ? (
<Button
style={{ marginRight: "16px" }}
onClick={onPrevClick}
>
Previous
</Button>
) : undefined}
<Button
primary={true}
disabled={
isLastStep
? !isPreviousStepsValid && !vehicleId
: false
}
onClick={formRenderProps.onSubmit}
>
{isLastStep ? "Submit" : "Next"}
</Button>
</div>
</div>
</FormElement>
</div>
)}
/>
</div>
</div>
<Notification notify={notify} setNotify={setNotify} />
</div>
);
};
export default Charging;
And here is the code for the component where i need these values. In the async componentDidMount function i want the url to be http://localhost:8765/evcharge/api/providers/${values.stationID}/${values.pointID} and get the params.
class OneStep extends React.Component {
data = [
{ text: "100%", id: 1 },
{ text: "75%", id: 2 },
{ text: "50%", id: 3 },
];
state = {
value: { text: "100%", id: 1 },
cost: {text: "", id: null}
};
providers = [];
async componentDidMount() {
const url = "http://localhost:8765/evcharge/api/providers";
const response = await fetch(url);
const data = await response.json();
for (var i = 0; i < data.providers.length; i++) {
this.providers.push({
text: "Provider: " +
data.providers[i]
.Title + " Cost: " + data.providers[i].kWhCost,
id: i + 1 ,
});
}
}
numberFrom = getRandomInt(30, 50, 0);
cost = getRandomInt(0.5, 2, 2);
handleChange = (event) => {
this.setState({
value: event.target.value,
});
console.log(this.data);
console.log(this.providers);
};
handleSecondChange = (event) => {
this.setState({
cost: event.target.value
})
}
render() {
return (
<div>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<div style={{ width: "50%", marginRight: "25px" }}>
<Button
style={{
width: "50%",
marginRight: "25px",
marginTop: "35px",
textTransform: "capitalize",
color: "#0779e4",
fontWeight: "600",
fontSize: "18px",
right: "50px",
}}
disabled={true}
look="flat"
>
From: {this.numberFrom}
</Button>
</div>
<Button
style={{
width: "50%",
marginRight: "25px",
marginTop: "35px",
textTransform: "capitalize",
color: "#0779e4",
fontWeight: "600",
fontSize: "18px",
}}
disabled={true}
look="flat"
>
Cost per kWh: {this.cost}
</Button>
</div>
<br />
<div style={{ display: "flex", justifyContent: "space-between" }}>
<div style={{ width: "25%", marginRight: "25px" }}>
<DropDownList
data={this.data}
dataItemKey="id"
value={this.state.value}
onChange={this.handleChange}
textField="text"
defaultItem={{ text: "To" }}
/>
</div>
<div style={{ width: "75%", marginRight: "25px" }}>
<DropDownList
data={this.providers}
dataItemKey="id"
value={this.state.providers}
onChange={this.handleSecondChange}
textField="text"
defaultItem={{ text: "Select Provider..." }}
/>
</div>
</div>
<br />
<div
style={{
display: "flex",
justifyContent: "space-between",
height: "250px",
}}
>
<div style={{ width: "50%", marginLeft: "15px" }}>
<Button
style={{
width: "50%",
marginRight: "25px",
marginTop: "35px",
textTransform: "capitalize",
color: "#ff5349",
fontWeight: "600",
fontSize: "18px",
right: "30px",
}}
disabled={true}
look="flat"
>
<CountUp
start={0}
end={parseInt(
(parseFloat(this.state.value.text) - this.numberFrom) *
this.cost
)}
duration={15}
useEasing={true}
decimals={2}
prefix="Expected Cost: "
suffix=" €"
useGrouping={true}
delay={3}
/>
</Button>
</div>
<div
style={{ width: "50%", marginRight: "25px", marginBottom: "450px" }}
>
<div className="g-container">
<div className="g-number">
<CountUp
start={30}
end={parseInt(this.state.value.text)}
duration={15}
useEasing={true}
decimals={2}
suffix=" %"
useGrouping={true}
delay={3}
/>
</div>
<div className="g-contrast">
<div className="g-circle"></div>
<ul className="g-bubbles">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
</div>
</div>
</div>
</div>
);
}
}
export const ChargingStep = <OneStep />;
Instead of exporting and using the rendered output of the OneStep component, just export and use the OneStep component itself, then you can pass whatever props you need to it.
Note there's no issue here with having a functional and class-based component as from the outside they're identical.
Start with the file that contains OneStep, change the export statement from export const ChargingStep = <OneStep />; to export const ChargingStep = OneStep; (or ideally just rename OneStep to ChargingStep and export it directly). Note you'll also have to do this with the other step components so they all work the same (but this is how React components should be exported and used anyway).
Then in the Charging component you can change the line in the return statement from {stepPages[step]} to something like:
const StepPage = stepPages[step];
return (
// ...
<StepPage relevantProp={value}/>
// ...
)
Or you can add special handling for just the ChargingStep step if you don't want to pass those same components to all the other steps, which I'd recommend here.
Further Refactoring
You might consider slightly changing the way you keep track of what step the user is on from a straight index lookup to using string names so you can tell which component is going to be rendered.
You could do something like this:
const stepPages = {
"SelectVehicle": SelectVehicle,
"PaymentMethod": PaymentMethod,
"ChargingStep": ChargingStep,
"Payment": Payment,
};
const stepPageNames = ["SelectVehicle", "PaymentMethod", "ChargingStep", "Payment"];
Then to get the step you're on:
const stepPageName = stepPageNames[step];
const StepPage = stepPages[stepPageName];
Then you can do things like:
let stepPage = <StepPage />
if (stepPageName === "ChargingStep") {
stepPage = <StepPage relevantProp={value}/>
}
And place stepPage in your return statement.
What I find most interesting is that {users.username} works, same with {users.profile.avatar} and even {users.profile.bio} but not what I need most of all: {users.emails.verified} and {users.emails.address}
I'm guessing that it has to do with the mapping of the data? Perhaps it's how I'm trying to call it? I tried {users['emails']['address']} as well.. but it doesn't work either. yet it works for {users['profile']['bio']} which makes it a bit bigger of a headache for me. Any help would be awesome, I'm all ears as to learning how to go about this!
import React from 'react';
import { Redirect } from 'react-router-dom';
import Avatar from '#atlaskit/avatar';
import DropdownMenu, {
DropdownItemGroup,
DropdownItem,
} from '#atlaskit/dropdown-menu';
import Tag, { TagColor } from '#atlaskit/tag';
import DynamicTable from '#atlaskit/dynamic-table'
import PageHeader from '#atlaskit/page-header';
import gql from 'graphql-tag';
import { useQuery } from '#apollo/react-hooks';
import Button, { ButtonGroup } from '#atlaskit/button';
const GET_USERS = gql`
query getUsers {
getUsers {
id
username
isAdmin
emails {
address
verified
}
profile {
bio
avatar
}
}
getUser {
id
}
}
`;
const Users = () => {
const actionsContent = (
<ButtonGroup>
<Button appearance="primary">Search Box Here Here</Button>
</ButtonGroup>
);
const { loading, error, data } = useQuery(GET_USERS);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
if (!data.getUser) {
return <Redirect to="/login" />;
}
const createHead = (withWidth: boolean) => {
return {
cells: [
{
key: 'avatar',
content: 'Avatar',
isSortable: false,
width: withWidth ? 2 : undefined,
},
{
key: 'username',
content: 'Username',
isSortable: true,
width: withWidth ? 18 : undefined,
},
{
key: 'email',
content: 'Email',
shouldTruncate: true,
isSortable: true,
},
{
key: 'tags',
content: 'Tags',
},
{
key: 'action',
content: 'Action',
shouldTruncate: true,
width: withWidth ? 2 : undefined,
},
],
};
};
const head = createHead(true);
var users = data.getUsers;
console.log(users)
for(let i = 0, l = users.length; i < l; i++) {
var rows = users.map((user: any) => ({
cells: [
{
key: 'avatar',
content: (
<span style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ marginRight: 8 }}>
<Avatar
name={user.username}
size="small"
src={user.profile.avatar}
/>
</div>
</span>
),
},
{
key: 'user',
content: (
<span style={{ display: 'flex', alignItems: 'center' }}>
{user.emails.address}
{user.emails.verified}
</span>
),
},
{
key: 'email',
content: (
<span style={{ display: 'flex', alignItems: 'center' }}>
{user.isAdmin}
<Tag text="Verified" color="greyLight" />
</span>
),
},
{
key: 'tags',
content: (
<span style={{ display: 'flex', alignItems: 'center' }}>
{user.isAdmin}
<Tag text="Admin" color="grey" />
</span>
),
},
{
key: 'lols',
content: (
<Button>More</Button>
),
},
],
}))
}
return (
<div>
<PageHeader
actions={actionsContent}
>
Users
</PageHeader>
<DynamicTable
head={head}
rows={rows}
isLoading={false}
defaultSortOrder="ASC"
loadingSpinnerSize="large"
/>
</div>
);
}
export default Users;
Well, I guess I could mark this question answered!
Another reason to define my types properly:
Ah! Just noticed the console log in the image. emails seems to be an array of email objects and not an object, so instead of {user.emails.verified} you should have {user.emails[0].verified}, same goes for address. Also, you may want to check if emails actually contains something and/or if it contains more than one email object – ibrahim mahrir
So for my specific use case: {user.emails[0].address} was what I was looking for as well as {String(user.emails[0].verified)} for returning boolean values!