I have recently started using react due to the performance it provides, so I'm not used to this new framework. I have searched on this exact topic but cannot find an answer.
Although the problem is very simple, (Just want to return the selected date).
Here's what I currently am trying to do:
let dateValue = format(new Date(), 'yyyy-MM-dd')+ 'T09:00:00.000Z';
const dateChanged = (value: any) => {
console.log("value: ", value);
dateValue = value;
};
const DateModal: React.FunctionComponent<any> = ({ isOpen, onClose }) => {
return (
<IonModal className="datemodal" isOpen={isOpen}>
<IonContent className="dateModalOpen">
<IonDatetime
locale="en-GB"
value={dateValue}
id="datetime"
onChange={() => dateChanged(datetime)}
showDefaultButtons={true}
min="1920"
max="2022"
className="calendar"
presentation="date"
>
<span slot="title">Date of Birth</span>
</IonDatetime>
</IonContent>
</IonModal>
);
};
I recieve an error on the "onChange" (Cannot find name 'datetime'.), this is what I used to do in Angular. I tried to use a template reference by doing "id=datetime", which in Angular was "#datetime". And in so doing would work inside the onChange event.
How do I make this work?
Thank you in advance!
Solved. On react you can use "Controller" to get the data from IonDateTime as follows:
const {
handleSubmit,
control,
setValue,
register,
getValues,
formState: { errors },
} = useForm({
defaultValues: {
fullname: "",
date: "",
gender: "MALE",
email: "",
},
});
console.log(getValues());
/**
*
* #param data
*/
const onSubmit = (data: any) => {
alert(JSON.stringify(data, null, 2));
};
const [ionDate, setIonDate] = useState("");
const dateChanged = (value: any) => {
let formattedDate = format(parseISO(value), "dd-MM-yyyy").replace(
/-/g,
"/"
);
setIonDate(formattedDate);
setshowDate({ isOpen: false });
};
const DateModal: React.FunctionComponent<any> = ({ isOpen }) => {
console.log(isOpen);
return (
<IonModal className="datemodal" isOpen={isOpen}>
<IonContent className="dateModalOpen">
<Controller
render={({ field }) => (
<IonDatetime
value={field.value}
onIonChange={(e) => dateChanged(e.detail.value)}
locale="en-GB"
onChange={dateChanged}
showDefaultButtons={true}
onIonCancel={() => setshowDate({ isOpen: false })}
min="1920"
max="2022"
className="calendar"
presentation="date"
>
<span slot="title">Date of Birth</span>
</IonDatetime>
)}
control={control}
name="date"
rules={{ required: "This is a required field" }}
/>
<IonButton type="submit">submit</IonButton>
</IonContent>
</IonModal>
);
};
};
You just need to use the ionOnChange handler:
<IonDatetime presentation="date"
id="datetime"
onIonChange={(e) => console.log(e)}>
</IonDatetime>
I have some code about react-country-region-selector, which is suppose to allow me to select the countries and regions, but countries do not get selected and regions remains a dash. Can someone tell me what went wrong with my code?
P.s I am using this site for my code, https://github.com/country-regions/react-country-region-selector
import { CountryDropdown, RegionDropdown } from 'react-country-region-selector';
import React, { useState } from "react";
const Locations = () => {
const [state, setState] = useState({
country: "",
region: ""
})
const selectCountry = (val) => {
setState({ state: val });
}
const selectRegion = (val) =>{
setState({ state: val });
}
const { country, region } = setState;
return(
<div>
<CountryDropdown
value={country}
onChange={(val) => selectCountry(val)} />
<RegionDropdown
country={country}
value={region}
onChange={(val) => selectRegion(val)} />
</div>
</div>
);
}
return default Locations;
What I currently have:
What I Need:
You just need to do some minor changes.
const selectCountry = (val) => {
setState({ country: val });
}
const selectRegion = (val) =>{
setState({ region: val });
}
const { country, region } = state;
return(
<div>
<CountryDropdown
value={country}
onChange={(val) => selectCountry(val)} />
<RegionDropdown
country={country}
value={region}
onChange={(val) => selectRegion(val)} />
</div>
</div>
I need to create a modify feature on a value. I have the input setup inside a modal but for some reason I can't seem to pass on the value to the initialValues.
My code starts with ActivityAreas
<Datatable
ref={this.datatable}
rows={this.props.activityAreas}
update={this.props.updateTable}
headers={[
{
key: "name",
title: () => t("entities.activityAreas.name"),
},
{
key: "action",
title: () => t("entities.activityAreas.action"),
render: (_, row) => (
<Flex>
<ClickableLink onClick={() => this.openModalDestroy(row)}>
{t("entities.activityAreas.destroy")}
</ClickableLink>
<ClickableLink onClick={() => this.openModalRename(row)}>
{t("entities.activityAreas.rename")}
</ClickableLink>
</Flex>
),
props: { align: "right" },
},
]}
type={ACTIVITY_AREAS}
/>
The line that says
this.openModalRename(row)
contains the actual line object with name and id. openModalRename looks like this right now
openModalRename = (row) => {
let modalProps = this.modalProps;
modalProps.row=row;
this.props.setModal(RenameActivityArea, modalProps, { width: '500px' })
}
It sends the data to the RenameActivityArea page
That page looks like this:
const RenameActivityArea = ({
values,
handleSubmit,
handleChange,
isValid,
hideModal,
isSubmitting,
setFieldValue,
...props
}) => {
const input = (name, inputProps) => (
<Input
{...getFormInputProps(
{
handleSubmit,
handleChange,
values,
isValid,
...props,
},
name,
inputProps
)}
/>
);
return (
<Form onSubmit={handleSubmit}>
<Header.H2>{t("settings.activityAreas.modActivityAreas")}</Header.H2>
{input("name", { label: "activityAreas.ActivityAreaName" })}
<ButtonRow
flex="0 0 auto"
flow="row"
justify="flex-end"
padding="10px 0px"
>
<Button type="button" outline onClick={hideModal}>
Fermer
</Button>
<Button
loading={isSubmitting}
disabled={!isValid}
onClick={handleSubmit}
>
{t("entities.activityAreas.save")}
</Button>
</ButtonRow>
</Form>
);
};
const initialValues = {
name: "",
};
const mapState = ({ entities }) => ({
activity_area: entities.activity_area,
});
const mapDispatch = (dispatch) => ({
onSubmit: (activity_area) => dispatch(updateActivityAreas(activity_area)),
});
RenameActivityArea.propTypes = {
modalProps: PropTypes.shape(),
handleSubmit: PropTypes.func,
handleChange: PropTypes.func,
hideModal: PropTypes.func,
isSubmitting: PropTypes.bool,
handleBlur: PropTypes.func,
errors: PropTypes.shape(),
touched: PropTypes.shape(),
isValid: PropTypes.bool,
};
const FormWrapped = withForm({
mapPropsToValues: () => initialValues,
validateOnChange: false,
validateOnBlur: true,
validationSchema: schema,
afterSubmit: (values, formik) =>
formik.props.afterModalSubmit(values, formik),
})(RenameActivityArea);
const Connected = connect(mapState, mapDispatch)(FormWrapped);
export default Connected;
I can get the value inside the box if I do this:
<Input {...getFormInputProps({
handleSubmit,
handleChange,
values: props.row, // <- This gives the value
isValid,
...props,
}, name, inputProps)}
/>
But then for some reason, I can't seem to be allowed to modify the value inside the input. It's like if it was read only or something. I am not sure thats the right way of approaching this anyway.
EDIT
getFormInputProps
export const getFormInputProps = (formProps, name, { label = name.replace('Attributes', ''), ...props } = {}) => {
const error = browseObject(formProps.errors, name)
const isTouched = browseObject(formProps.touched, name)
return {
onChange: formProps.handleChange,
label: label && t(`entities.${label}`),
error,
name,
value: browseObject(formProps.values, name) || '',
onBlur: formProps.handleBlur,
touched: isTouched,
...props,
}
}
withForm
export default withForm({
mapPropsToValues,
validateOnChange: false,
validateOnBlur: true,
validationSchema: schema,
afterSubmit: (_, { props: { history } }, { payload: { user } }) => {
history.push(getRedirect(user))
},
})
By adding the value inside the input field i can see it inside the modal but I can't type or change it. The Input does not have any read-only restrictions but still does not allow typing. So I might not be doing this the right way.
It looks like it uses Formik
https://formik.org/docs/overview
EDIT:
The utils/form is the following. As you can see it uses Formik
import { withFormik } from 'formik'
import { debounce } from 'lodash'
import objectToFormData from 'object-to-formdata'
import * as yup from 'yup'
import API from '../config/api'
import t from './translate'
import { DEFAULT_TEXT_WRAP, DEFAULT_TEXT_WRAP_THRESHOLD } from '../config/constants'
yup.setLocale({
mixed: {
required: () => t('errors.fieldIsRequired'),
},
})
const DEFAULT = (afterSubmit = () => { }) => ({
handleSubmit: (values, formik) => {
formik.props.onSubmit(values).then((action) => {
formik.setSubmitting(false)
afterSubmit(values, formik, action)
})
},
})
export default ({
afterSubmit = (values, { props: { afterSubmit: after } }) => after && after(values),
...attrs
}) => withFormik(Object.assign({}, DEFAULT(afterSubmit), attrs))
export async function checkExists(url, params) {
const response = await fetch(API.getUrl(url, params), {
method: 'GET',
headers: API.headers(),
})
return response
}
export const asyncSelectProps = formProps => key => ({
onChange: (items) => {
formProps.setFieldValue(key, items.map(item => item.id), false)
if (!formProps.touched[key]) {
formProps.setFieldTouched(key, true, false)
}
formProps.setFieldError(key, (!items.length) ? t('errors.mustContainAtLeastOneItem') : undefined, false)
},
touched: formProps.touched[key],
error: formProps.errors[key],
})
/* eslint prefer-arrow-callback: 0 */
/* eslint func-names: 0 */
yup.addMethod(yup.string, 'asyncUnique', function (url, body, message) {
return this.test({
name: 'asyncUnique',
message,
test: debounce(async (value) => {
if (value && value.length > 0) {
const response = await checkExists(`/exists/${url}`, body(value))
const { exists } = await response.json()
return !exists
}
return true
}, 500),
})
})
export const browseObject = (object, path) => {
const parsePath = (path.constructor === String) ? path.split('.') : path
const [key, ...rest] = parsePath
if (object && object[key] !== undefined) {
const next = object[key]
return (rest.length > 0) ? browseObject(next, rest) : next
}
return null
}
export const getFormInputProps = (formProps, name, { label = name.replace('Attributes', ''), ...props } = {}) => {
const error = browseObject(formProps.errors, name)
const isTouched = browseObject(formProps.touched, name)
return {
onChange: formProps.handleChange,
label: label && t(`entities.${label}`),
error,
name,
value: browseObject(formProps.values, name) || '',
onBlur: formProps.handleBlur,
touched: isTouched,
...props,
}
}
export const preventPropagate = callback => (event) => {
event.stopPropagation()
callback(event)
}
export const extractFiles = (files, multiple = false) => (multiple ? files : files[0])
export const fileInputHandler = (formProps, name, multiple = false) => (event) => {
const file = extractFiles(event.target.files, multiple)
formProps.setFieldValue(name, file)
}
export const wrapText = (
text,
maximum = DEFAULT_TEXT_WRAP,
threshold = DEFAULT_TEXT_WRAP_THRESHOLD,
) => {
if ((text.length + threshold) > maximum) {
return `${text.slice(0, maximum)}...`
}
return text
}
export const toFormData = body => objectToFormData(body)
export const PHONE_NUMBER_REGEX = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/
export const ZIP_CODE_REGEX = /^[A-Za-z]\d[A-Za-z][ -]?\d[A-Za-z]\d$/
EDIT:
So instead of using the const to render my input, I used directly an input field and no I can edit. But why did the const cause the problem?
const RenameActivityArea = ({
values,
touched,
handleSubmit,
handleChange,
handleBlur,
isValid,
hideModal,
isSubmitting,
setFieldValue,
...props
}) => {
return (
<Form onSubmit={handleSubmit}>
<Header.H2>{t('settings.activityAreas.modActivityAreas')}</Header.H2>
<input
type="text"
onChange={handleChange}
onBlur={handleBlur}
value={values.name}
name="name"
/>
<ButtonRow flex="0 0 auto" flow="row" justify="flex-end" padding="10px 0px">
<Button type="button" outline onClick={hideModal}>Fermer</Button>
<Button loading={isSubmitting} disabled={!isValid} onClick={handleSubmit}>{t('entities.activityAreas.save')}</Button>
</ButtonRow>
</Form>
)
}
I have an input 'A' that fetches address data from an API and auto fills inputs 'B' 'C' and 'D' based on that data, but after the inputs have been filled and I try to send that form to my backend, none of those auto filled inputs are sent, just the input 'A' is sent. Furthermore, if i manually edit any of the inputs (remove a char, add a space, change the value) the ones that I edited get sent to my backend.
I'm using a reducer to store the state. The inputs that I'm using are all just normal react-admin TextInput components.
Here's the code:
const AutoFill = () => {
const [searching, setSearching] = useState(false);
const [error, setError] = useState(false);
const [stateData, setStateData] = useReducer(
(state, newState) => ({ ...state, ...newState }),
{
cep: ' - ',
address: '',
number: '',
neighborhood: '',
city: '',
state: '',
}
);
const FormControl = (event) => {
const { name, value } = event.target;
setStateData({ [name]: value });
};
const SearchControl = (event) => {
const { name, value } = event.target;
setStateData({ [name]: value });
if (value && !value.includes('_')) {
setSearching(true);
setStateData({ state: '...' });
setStateData({ city: '...' });
setStateData({ neighborhood: '...' });
setStateData({ address: '...' });
cep(value.replace('-', '')).then(
(result) => {
setSearching(false);
setError(false);
setStateData({ state: result.state });
setStateData({ city: result.city });
setStateData({ neighborhood: result.neighborhood });
setStateData({ address: result.street });
},
() => {
setSearching(false);
setError(true);
setStateData({ state: '' });
setStateData({ city: '' });
setStateData({ neighborhood: '' });
setStateData({ address: '' });
}
);
}
};
return (
<>
<TextInput
source="cep"
error={error}
value={stateData.cep}
onChange={SearchControl}
/>
<TextInput
source="address"
disabled={searching}
value={stateData.address}
onChange={FormControl}
/>
<TextInput
source="number"
disabled={searching}
value={stateData.number}
onChange={FormControl}
/>
<TextInput
source="neighborhood"
disabled={searching}
value={stateData.neighborhood}
onChange={FormControl}
/>
<TextInput
source="state"
disabled={searching}
value={stateData.state}
onChange={FormControl}
/>
<TextInput
source="city"
disabled={searching}
value={stateData.city}
onChange={FormControl}
/>
</>
);
};
export const Create = (props) => {
return (
<Create {...props}>
<SimpleForm>
<NumberInput label="Value" source="price" />
<AutoFill />
<RichTextInput label="Description" source="description" />
</SimpleForm>
</Create>
);
};
You're going to need to use React Final Form's FormState and Form solutions. Will use snippets of my code for example.
1) Grab the form values
const formState = useFormState();
const form = useForm();
const {
asset_system_parent_id: majorSystem,
classification,
} = formState.values;
2) Setup useEffect that will observe changes to a form field:
useEffect(() => {
const setFluidEnd = async () => {
DO SOMETHING!!!!!
};
if ('Fluid End Maintenance' === classification) {
setFluidEnd();
}
}, [classification, form, notify]);
3) Use form.change (+ form.batch if you need to update multiple inputs)
useEffect(() => {
const setFluidEnd = async () => {
await requestGetList('asset-systems', 'id', 'ASC', 500, {
description: 'Fluid End',
relationship: 'parent',
})
.then(res => {
form.change('asset_system_parent_id', res.data[0].id);
})
.catch(error => {
notify(`System Assets not found`, 'warning');
});
};
if ('Fluid End Maintenance' === classification) {
setFluidEnd();
}
}, [classification, form, notify]);
You can read more about the api here: https://final-form.org/docs/final-form/types/FormApi
Please use this code.
-index.js file
import axios from "axios";
export const setInputValue = (data) => {
return axios.get(`https://www.example.com/profile`)
.then((response) => {
return response.data;
});
};
-component.js
return setInputValue(value).then(() => {
this.setState(() => ({
loading: false
}));
});
...
render(){
return (
...
<input type="text" onClick={e => this.onClick(e)} value={this.state.value}/>
..
)}
...
react-admin.php
...
public function setInputValue(value)
{
try {
$user->set(value);
return response()->json(["result" => "successfully!"]);
} catch (\Exception $e) {
return getErrorResponse($e);
}
}
The input field displays the value saved in local storage, but I can't edit the value in the input field and I don't know why.
I don't want to use a placeholder as I want to be able to edit the values.
import React, { useRef, useState } from 'react';
const ProfileComponent: React.FC = () => {
let email = useRef<HTMLInputElement>(null);
const saveEmail = () => {
localStorage.setItem('email', email)
}
// tslint:disable-next-line: no-any
const update = (event: any) => {
if (event.target.name === 'email') {
setState({ ...state, email: event.target.value });
} else if (event.target.name === 'fullName') {
setState({ ...state, fullName: event.target.value });
}
};
interface StateInterface {
email: string;
}
const [state, setState] = useState<StateInterface>({
email: localStorage.getItem('email') || '',
});
return (
<input type='text' name='fullName' ref={fullName} onChange={update} value={state.fullName} />
<input type='text' name='email' ref={email} onChange={update} value={state.email} />
<button onClick={saveEmail}></button>
)
}
There are a few issues with the code you have provided
1) You should wrap the DOM elements with React Fragments (<> </>)
2) Instead of setting the type of event as any, you might want to use React.FormEvent<HTMLInputElement>.
3) You should use localStorage.setItem('email', state.email) instead of localStorage.setItem('email', email), since email is a property as part of the state object, thus you will have to reference it in order to access the values.
Here are the full changes below:
interface StateInterface {
email: string;
fullName: string;
}
const ProfileComponent: React.FC = () => {
let email = useRef<HTMLInputElement>(null);
let fullName = useRef<HTMLInputElement>(null);
const [state, setState] = useState<StateInterface>({
email: 'aa#gmail.com' || '',
fullName: 'aa' || '',
});
const saveEmail = () => {
localStorage.setItem('email', state.email)
console.log(state);
}
const update = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.name === 'email') {
setState({ ...state, email: event.target.value });
} else if (event.target.name === 'fullName') {
setState({ ...state, fullName: event.target.value });
}
};
return <>
<input type='text' name='fullName' ref={fullName} onChange={update} value={state.fullName} />
<input type='text' name='email' ref={email} onChange={update} value={state.email} />
<button onClick={saveEmail}>save</button>
</>
}
You have to have an onChange in your input
return (
<input type='text' name='email' ref={email} onChange={e => setState({email: e.target.value})}
value= {state.email} />
<button onClick={saveEmail}></button>
)