How can I use persian numbers with react-number-format - javascript

I created this component with react-number-format
const NumberFormatCustom = React.forwardRef(function NumberFormatCustom(
props,
ref
) {
const { onChange, ...other } = props;
return (
<NumericFormat
{...other}
getInputRef={ref}
onValueChange={(values) => {
onChange({
target: {
name: props.name,
value: values.value,
},
});
}}
maxLength={11}
allowLeadingZeros
// isNumericString
/>
);
});
and I passed it to my TextField component of MUIv5.
InputProps={{
inputComponent: NumberFormatCustom,
}}
but it doesn't work. the onChange prop of my TextField don't work at all
I tried some approaches like this :
e.target.value.replace(/[۰-۹]/g, (d) => "۰۱۲۳۴۵۶۷۸۹".indexOf(d))
but it didn't work because onChange is not working. I tried to add replace function to value part of NumericFormat but it didn't work either.
how can I handle it?

I use a Persian font like Vazir or.... That show digits Persian. And we can use this rule to check input :
/^[0-9]*$/
This rule just accept number.

Related

Cannot read properties of undefined (reading 'target)

first post here. I tried searching around the forum but none of the answers seems to work in my case.
I'm pretty new to React, and have an issue with my onChange handler.
In my parent component I have the following:
// User registration - Onchange handler
const handleChange = (value, event) => {
// console.log(event.target.value);
setFormValue({
...formValue,
[event.target.name]: value,
});
};
And basically the parent component renders a multi step registration form, which renders the different components based on a state, here is the conditional for the username:
{step === 1 ? <Username handleChange={handleChange} formValue={formValue} /> : null}
In the child I have the following input component:
export const Username = ({formValue, handleChange}) => {
const username = formValue;
return (
<View>
<Input
placeholder="Username..."
onChangeText={handleChange}
value={formValue.username}
name={username} />
</View>
)
}
And the input component:
const Input = (props, name) => {
return (
<Animated.View style={[styles.inputSection, inputAnimated]}>
<TextInput style={styles.input} placeholder={props.placeholder} placeholderTextColor={theme.colors.lightP} fontSize={16} {...props}/>
</Animated.View>
)
}
I'm not sure why I get the error:
Cannot read properties of undefined (reading 'target)
Would appreciate any help.
Console.log of event.target returns undefined.
Here in the documentation you can read for the onChange event
You are using onChangeText
So onChangeText has just text value, but if you want to have the native event you have to use onChange.
Then access it in you method
const handleChange = ({ nativeEvent: { eventCount, target, text} }) => {
// console.log(event.target.value);
setFormValue({
...formValue,
[target]: text,
});
};
EDITED
Since asked again how to access the name of text input.
Here this link shows how to handle text input
Also you can do it like this:
For example in your method you are giving a name from parameters, you can pass that name to the handler parameter :
export const Username = ({formValue, handleChange}) => {
const username = formValue;
return (
<View>
<Input
placeholder="Username..."
onChangeText={(value)=>handleChange(value,username)} {/* here username is the name you are passing to the value */}
value={formValue.username}
name={username} />
</View>
)
}
// then in your handle method
const handleChange = (value,name) => {
setFormValue({
...formValue,
name: value,
});
};
This happens because onChangeText passes the input's value automatically to the function that's assigned to it, not passing the event itself
If you want to use the event => use onChange instead of onChangeText

Formik: target is undefined when passing Material UI Date Picker as prop to React Component

I'm trying to create a reusable component. where I am passing the form fields as prop. when I click on datepicker field. I'm getting this error:
TypeError: target is undefined
How can I fix this?
Here's how my input component looks like:
import { useField } from 'formik';
import { DatePicker } from '#material-ui/pickers';
export const DatePickerField = ({ label, ...props }) => {
const [field, meta] = useField(props);
return (
<DatePicker label={label} fullWidth {...field} {...props} />
);
};
Here's how the reusable component looks like:
import { Form } from 'formik'
export const ReusableComp = ({ fields }) => (
<Form noValidate>
{fields}
</Form>
)
Here's where I am using this component:
export const App = () => (
<ReusableComp fields={
<div className='mb-3'>
<DateTimePickerField
label='Start DateTime'
name='start_date_time'
/>
</div>
} />
)
Result of console.log(fields)
In my case I was using the Material UI DesktopDatePicker (with Typescript), and defining the onChange prop like this solved it:
onChange={(value): void => {
formik.setFieldValue("dueDate", value);
}}
You have to replace "dueDate" with your formik value name.
After lots of research and thanks #Rosen Tsankov for pointing my attention to the onChange function.
I have seen questions about the same error on SO which are not answered. so this may help them and anyone in future facing this error.
As #Rosen Tsankov have said the material ui DatePicker component returns the date value as the first argument of the onChange function.
the field returned from useField have the following: name, value, onChange, onBlur.
the onChange function returned from useField expects the first argument to be an event which in this case is the date value. that's why we get the error:
TypeError: target is undefined
Because formik is trying to access target property and date has no property target. something like this date.target and this is undefined
so here's how I have fixed it. instead of spreading the field. I have added name and value from field to DatePicker. then I have used the setFieldValue from formik to manually update the input value like so.
import { DatePicker } from '#material-ui/pickers';
import { useField, useFormikContext } from 'formik';
export const DatePickerField = ({ label, ...props }) => {
const [field, meta] = useField(props);
const { setFieldValue } = useFormikContext();
return (
<DatePicker
fullWidth
{...props}
label={label}
name={field.name}
value={field.value}
helperText={meta.error}
error={meta.touched && Boolean(meta.error)}
onChange={(value) => setFieldValue(field.name, value)}
/>
);
};
DatePicker expects onChange((date) => ...) but you are passing formik handler wich expects onChange((event) => ....)

how to update material ui textfield correctly onChange and put value into Json object?

I am trying to update the title and body objects within my array with the value provided from the input fields that mapped to the array length. The body object updates perfectly but the title updates with only the current input value. For instance, if I were to try and type "the".
The title object would change as follows "title: "t"" => "title: "h""
=> "title: "e".
Desired output would be "title: "t"" => "title: "th"" => "title:
"the".
This works with the body so I am confused why it is not with the title maybe I have missed something.
export const NewsArticlesPage = () => {
const [newsArticlesJson, setNewsArticlesJson] = useContext(
NewsArticlesContext
)
const [numberOfSteps, setNumberOfSteps] = useState(0)
const stepsMap = Array.apply(
null,
Array(numberOfSteps).fill({ title: '', body: '' })
)
let stepsArray = { ...stepsMap } as StepsMapProps
const updateTileArray = (index: number, titleEventData: string) => {
const stepsArrayData = { ...stepsArray }
stepsArray = {
...stepsArrayData,
[index]: { ...stepsArrayData[index], title: titleEventData },
}
console.log(stepsArray[index])
console.log(stepsArray[index]?.title)
}
const updateRichTextArray = (index: number, richTextEventData: string) => {
const stepsArrayData = { ...stepsArray }
stepsArray = {
...stepsArrayData,
[index]: { ...stepsArrayData[index], body: richTextEventData },
}
console.log(stepsArray[index])
console.log(stepsArray[index]?.body)
}
return (
<NewsArticlesWrapper>
<TextField
type="number"
label="Number of steps"
value={numberOfSteps}
InputProps={{ inputProps: { min: 0 } }}
onChange={(e) => setNumberOfSteps(Number(e.target.value))}
/>
{stepsMap.map((n, index) => (
<>
<Typography key={'heading' + index}>Step: {index + 1}</Typography>
<TextField
key={'title' + index}
type="text"
label="Title"
value={stepsArray[index]?.title}
onChange={(titleEventData) =>
updateTileArray(index, titleEventData.target.value)
}
/>
<ReactQuill
key={'quill' + index}
theme="snow"
value={stepsArray[index]?.body}
modules={modules}
onChange={(richTextEventData) =>
updateRichTextArray(index, richTextEventData)
}
/>
</>
))}
<Button
variant="contained"
colour="primary"
size="medium"
onClick={updateNewsArticleJson}
>
Submit Article
</Button>
</NewsArticlesWrapper>
)
}
If any extra information is needed to help me please ask.
Code Sandbox set up to replicate issue: https://codesandbox.io/embed/kind-buck-shg2t?fontsize=14&hidenavigation=1&theme=dark
<TextField /> and <ReactQuill /> input components are defined in a ‘controlled component’ way. The object they’re setting / getting values to is stepsArray which you have created as a normal object
let stepsArray = { ...stepsMap } as StepsMapProps
The onChange handler on both these components are changing values on this object
stepsArray = {
...stepsArrayData,
[index]: { ...stepsArrayData[index], title: titleEventData },
}
but, there isn't a re-render, because React re-renders when state changes. stepsArray is a normal js object, and although the value is changing, it is not causing a re-render. This is the reason, you cannot see the value you type on Title <TextField />component.
To solve this, make the object where you’re setting/getting values to, with useState, and use the set(State) function provided by the useState hook accordingly to update values inside the object. This will cause a re-render whenever input's value changes, because now you will be changing the object created through useState's setter function and your UI will be in sync with the state.

How do I identify a material-ui slider in react?

I want multiple material-ui sliders in one react component sharing a common event handler. However, to make this work, I would need to identify the originating slider. From the API documentation I can't see how that is achieved. I've tried applying id and name attributes to the <Slider>-component, yet I'm not seeing these in the synthesized event in the event handler.
handleChange = (event, value) => {
console.log(event); // 'Id' and 'name' attributes in 'target' are empty
this.setState({ value });
};
render() {
const { classes } = this.props;
const { value } = this.state;
return (
<div className={classes.root}>
<Typography id="label">Slider label</Typography>
<Slider
classes={{ container: classes.slider }}
value={value}
aria-labelledby="label"
onChange={this.handleChange}
/>
</div>
);
}
This is fetched from the official demo project:
https://codesandbox.io/s/4j9l9xn1o4
Any help would be greatly appreciated!
You can format your state like so:
state = {
slider1: 50, //slider1 is the name of the first slider
slider2: 50, //slider2 is the name of the second slider
}
After that, you have 2 ways to set the state when the value of the slider is changed:
(Update: This method doesn't work! However I will leave it here for future reference) By using HTML attribute id, then access it by using event.target.id. The whole handleChange method would look like this:
handleChange = (e, value) => {
this.setState({
[e.target.id]: value
});
}
By passing then name of the slider straight to the handleChange method, and it would be like this:
handleChange = name => (e, value) => {
this.setState({
[name]: value
});
}
Overall, your component should be:
class SimpleSlider extends Component {
state = {
slider1: 50,
slider2: 50
};
handleChange = name => (e, value) => {
this.setState({
[name]: value // --> Important bit here: This is how you set the value of sliders
});
};
render() {
const { classes } = this.props;
const { slider1, slider2 } = this.state;
return (
<div className={classes.root}>
<Typography id="label">Slider label</Typography>
<Slider
classes={{ container: classes.slider }}
value={slider1}
aria-labelledby="label"
onChange={this.handleChange("slider1")}
/>
<Slider
classes={{ container: classes.slider }}
value={slider2}
aria-labelledby="label"
onChange={this.handleChange("slider2")}
/>
</div>
);
}
}
See it in action: https://codesandbox.io/s/4qz8o01qp4
Edit: After running the code I found that the #1 doesn't work because the id attribute is not being passed down to the event target

How to listen to onChange of the Field component in React-Final-Form?

Redux-form "Field" component provides onChange property. A callback that will be called whenever an onChange event is fired from the underlying input. This callback allows to get "newValue" and "previousValue" for the Field.
React-final-form "Field" component doesn't have this property.
So, how I can get the same functionality?
React-final-form handles this functionality with a tiny external package.
Basically it is an additional component to add inside the form that binds to the element using its name:
<Field name="foo" component="input" type="checkbox" />
<OnChange name="foo">
{(value, previous) => {
// do something
}}
</OnChange>
The current documentation can be found here:
https://github.com/final-form/react-final-form-listeners#onchange
The idea under change detection is to subscribe to value changes of Field and call your custom onChange handler when value actually changes. I prepared simplified example where you can see it in action. Details are in MyField.js file.
As the result you can use it just as with redux-form:
<MyField
component="input"
name="firstName"
onChange={(val, prevVal) => console.log(val, prevVal)}
/>
2022 JANUARY UPDATE
While the code above still works (check the sandbox version) there is a case when the solutions requires more tweeks around it.
Here is an updated sandbox with an implementation via the hooks. It's based on a useFieldValue hook and OnChange component as a consumer of this hook. But the hook itself can be used separately when you need previous value between re-renders. This solution doesn't rely on meta.active of the field.
// useFieldValue.js
import { useEffect, useRef } from "react";
import { useField } from "react-final-form";
const usePrevious = (val) => {
const ref = useRef(val);
useEffect(() => {
ref.current = val;
}, [val]);
return ref.current;
};
const useFieldValue = (name) => {
const {
input: { value }
} = useField(name, { subscription: { value: true } });
const prevValue = usePrevious(value);
return [value, prevValue];
};
export default useFieldValue;
// OnChange.js
import { useEffect } from "react";
import useFieldValue from "./useFieldValue";
export default ({ name, onChange }) => {
const [value, prevValue] = useFieldValue(name);
useEffect(() => {
if (value !== prevValue) {
onChange(value, prevValue);
}
}, [onChange, value, prevValue]);
return null;
};
Another nice option is this answer: https://stackoverflow.com/a/56495998/3647991
I haven't used redux-form, but I added a super simple wrapper around the Field component to listen to onChange like this:
const Input = props => {
const {
name,
validate,
onChange,
...rest
} = props;
return (
<Field name={name} validate={validate}>
{({input, meta}) => {
return (
<input
{...input}
{...rest}
onChange={(e) => {
input.onChange(e); //final-form's onChange
if (onChange) { //props.onChange
onChange(e);
}
}}
/>
)}}
</Field>
);
};
One could use the Field's parse attribute and provide a function that does what you need with the value:
<Field
parse={value => {
// Do what you want with `value`
return value;
}}
// ...
/>
You need to use the ExternalModificationDetector component to listen for changes on the field component like this:
<ExternalModificationDetector name="abc">
{externallyModified => (
<BooleanDecay value={externallyModified} delay={1000}>
{highlight => (
<Field
//field properties here
/>
)}
</BooleanDecay>
)}
</ExternalModificationDetector>
By wrapping a stateful ExternalModificationDetector component in a
Field component, we can listen for changes to a field's value, and by
knowing whether or not the field is active, deduce when a field's
value changes due to external influences.
Via - React-Final-Form Github Docs
Here is a sandbox example provided in the React-Final-Form Docs: https://codesandbox.io/s/3x989zl866

Categories

Resources