Reactjs - input mask without jquery - reactjs

I'm trying to make an input mask without using jquery. Only using the states of reactjs.
But, whenever he formats the field, I can't erase what was written on it. I will put my code below, in case you have any other way to do this I also accept.
The mask is to be in the format 9999999-9
My code:
const [accountRegex, setAccountRegex] = React.useState('');
function handleAccountRegex(event) {
setAccountRegex(event.target.value)
if (accountRegex.length === 8) {
setAccountRegex(accountRegex.replace(/(\d{7})(\d{1})/g, "$1-$2"))
}
}
<Input name="account" label="Conta" required={true} onChange={handleAccountRegex} value={accountRegex} maxLength='8' />

I've made some changes:
function handleAccountRegex(event) {
// first make sure input doesn't contain the "-" character in it
// because it will mess up the "backspace" key functionality
let val = event.target.value.replace("-", "")
// if input value is 8 digits mask it
if (val.length === 8) {
setAccountRegex(val.replace(/(\d{7})(\d{1})/g, "$1-$2"))
}
// else just store the changes
else setAccountRegex(val)
}

Related

why react user input type text make number get NaN

I am learning and try to make use of user input (textbox) type text to type in number and which will auto add commas and fixed to 2 decimal places using regex. It's work at first, but when I click the same textbox again (to edit the input) then I click other textbox or outside of the same textbox, the value change to NaN. Can anyone help me on this? Below is my code:
<InputForm
inputtype="text"
name="initialBalance"
placeholder=""
value={props.values.initialBalance}
onChange={async (name,value) => {
props.setFieldValue(name, value);
}}
onBlur={event => {
const formatted = props.values['initialBalance'];
var deciFormatted = Number(formatted).toFixed(2);
props.setFieldValue('initialBalance',deciFormatted?deciFormatted.replace(/\B(?=(\d{3})+(?!\d))/g, ',') :Number.parseFloat(0).toFixed(2));
props.handleBlur(event);
}}
onKeyPress={(event) => {
if (/[^0-9.]|(?<=\..*)\./.test(event.key)) {
event.preventDefault();
}
}}
className={props.errors.initialBalance && props.touched.initialBalance && "is-invalid"}
disabled={props.disabledForm}
isInvalid={!!props.errors.initialBalance}
Actually, I have found the solution to the problem. I just add below method:
Number.isNaN(value)
which will change NaN to previous input that user type in. So, the code will be like this:
onBlur={event => {
const formatted = props.values['initialBalance'];
var deciFormatted = Number(formatted).toFixed(2);
var newValue = (isNaN(deciFormatted) ? formatted : deciFormatted);
props.setFieldValue('initialBalance',deciFormatted?deciFormatted.replace(/\B(?=(\d{3})+(?!\d))/g, ',') :Number.parseFloat(0).toFixed(2));
props.handleBlur(event);

How would I re-render the list everytime the state changes for this text filter using react hooks

I have updated this question with clearer and more concise code on 15/03/22.
I have a text filter and it filters an array (displaying a list) correctly if it matches something in the displayed list, but as the user deletes character by character the filter does not change. I need it to keep matching as the user delete's chars.
Here is the filter which is set as the onChange for the text field:
const [searchInputTitle, setSearchInputTitle] = useState<string>('');
const [filteredItemsArrayState, setFilteredItemsArrayState] = useState<IListItems[]>(props.requests);
const searchTitles = () => {
let filteredArrayByTitle: IListItems[] = [];
filteredArrayByTitle = theRequestsState.filter((item) => {
return item.Title && item.Title.toLowerCase().indexOf(searchInputTitle.toLowerCase()) >= 0;
});
console.log(searchInputTitle, 'searchInputTitle');
if (searchInputTitle && searchInputTitle.length > 0) {
setTheRequestsState(filteredArrayByTitle);
setIsFiltered(true);
} else if (searchInputTitle && searchInputTitle.length === 0) {
const AllItems = props.requests;
let sortedByID: IListItems[] = AllItems.sort((a, b) => a.Id > b.Id ? 1 : -1);
setTheRequestsState(sortedByID);
setIsFiltered(false);
}
};
<TextField
onChange={searchTitles}
value={searchInputTitle}
/>
useEffect(() => {
_onTitleFilterChange(null, searchInputTitle);
if (isFiltered == true) {
setFunctionalArea(null, null);
setRequestingFunction(null, null);
}
}, [isFiltered, searchInputTitle]);
////////
<DetailsList className={styles.DetailsList}
items={filteredItemsArrayState.slice((ListPage - 1) * 50, ((ListPage * 50)))}
/>
Can anyone see why the render is not updating on deletion of char and what I could use to do so?
Update: As I type a character into the search I can see it's finding the searched for char/word and also if I delete chars now it searches and finds actively, but as soon as I stop typing it reverts back to the original array of items.
Can you try to filter the array you give to the DetailsList so you never lost the data ..?
filteredItemsArrayState.filter(s => {
if (searchInputTitle.length === 0)
return true
else
return s.Title.toLowerCase().match(searchInputTitle.toLowerCase())
}).map(....)
Found the reason. Thanks for all your help.
The issue was the setIsFiltered(true); in the title filter function. Other filters were running based on this line and these other filters were recalling the unfiltered list everytime a key was pressed. I removed this line and the issue was fixed.
I have come to realise that useEffect is almost completely mandatory on most of my projects and is React hooks are quite a departure from the original React syntax.

validation user input in react js

I am trying to figure out how to validate the user's input by checking the length of their input string. Right now the code will validate if the user's input whatever is string or integer. But I need help with validate the length of their input string and give them an error if the length of the input string is less than 2.
const handleText = (event) => {
let letters = /^[A-Za-z]+$/;
if (event.target.value.match(letters)) {
setText(event.target.value);
}else
setText("error");
}
return(
<div>
<TextField onChange = {handleText} label={"Name"}/>
{text}
</div>
);
}
May be this code will help. You can play with it on this link sandbox i created. You can validate length through onSubmit event by using the length property.

Using Jest and Enzyme to call a function

I am using Jest and Enzyme to test a React component. I am trying to test my form validation rules when submitting a form. The tests need to cover all possible cases of this function
const handleSubmit = event => {
event.preventDefault();
const { createPassword, confirmPassword } = event.target.elements;
if (createPassword.value !== confirmPassword.value) {
setPassValidationError("*Passwords must match!");
} else if (createPassword.value.length < 8) {
setPassValidationError("*Passwords must be at least 8 characters long!");
} else if (createPassword.value.search(/[A-Z]/) < 0) {
setPassValidationError(
"*Passwords must contain at least one uppercase letter!"
);
} else if (createPassword.value.search(/[!##$%^&*]/) < 0) {
setPassValidationError(
"*Passwords must contain at least one special character!"
);
} else {
props.updatePassword({
uid: props.uid,
token: props.token,
new_password: createPassword.value
});
event.target.reset();
}
};
This function is pretty straight forward createPassword and confirmPassword are the values for 2 different input fields. When the form is submitted and this function gets called I am testing the password on different criteria. If the password is not strong enough, the setPassValidationError hook is called and updates a state variable.
I am currently trying to test the function with a password shorter than 8 characters.
it("passwords must be 8 char long", () => {
const wrapper = mount(<NoAuthPasswordChange />);
const passInput = wrapper.find("#create-password");
const confirmPass = wrapper.find("#confirm-password");
passInput.simulate("change", { target: { value: "QQQQQQ" } });
confirmPass.simulate("change", { target: { value: "QQQQQQ" } });
const submitButton = wrapper.find("#submit-button");
submitButton.simulate("click");
expect(wrapper.find("#password-validation-error").text()).toContain(
"*Passwords must be at least 8 characters long!"
);
});
Jest is telling me that #password-validation-error cannot be found (expected 1 node found 0). Now this particular part of the code is only rendered if passValidationError has data.
{passValidationError ? (
<h2
className={styles.passwordError}
id="password-validation-error"
>
{passValidationError}
</h2>
) : null}
I'm not sure if I just have a simple bug in my test or if something more advanced needs to be done in order to use Jest and have a function call a hook update.
Edit: I am beginning to wonder if the event parameter required by the handleSubmit function is problematic due to the function being called by Jest.
This can be cause by not updating the component itself. Have you tried to force your wrapper to be re-rendered:
https://airbnb.io/enzyme/docs/api/ShallowWrapper/update.html
https://airbnb.io/enzyme/docs/api/ReactWrapper/update.html
I have found a solution to my issue. The test needs to call the form submission on the form element itself and not via a button click. So instead of submitButton.simulate("click") I need to simulate a submit on my form element. I am unsure why this solution works and the posted code does not.

Leading Zeroes in React form

I'm an intermediate React developer. I'm building a form with React, Redux, and React Number Format. For the most part things are going well, but I'm a bit hung up on how to get rid of leading zeroes for my component. I think I understand the problem but I'm not sure where the right place to intervene is.
My app is deployed here. Here's my code defining my number field (from customInput.js)
<NumberFormat
value = {this.props.input.value || 0}
onFocus = {()=>{}}
onBlur = {this.handleChange.bind(this)}
onChange = {this.handleChange.bind(this)}
onInput = {this.handleChange.bind(this)}
thousandSeparator = {true}
prefix = {this.props.prefix}
suffix = {this.props.suffix}
decimalScale = {1}
isAllowed={(values) => {
const {floatValue} = values;
if (typeof floatValue==='undefined') {
return true;
}
if (this.props.maximum) {
return floatValue <= this.props.maximum;
} else {
return true;
}
}}
/>
It may be more helpful to look at my full code on GitHub.
This is my diagnosis of the problem. My input is taking its value from props, not from state. I found this easier when creating some of the field logic to avoid double renderings. When a field is changed, the change is dispatched to the Redux store. The field's state is not really used at all. I'm not sure if this is good practice, but it has worked well for me.
The problem is that when I dispatch a change adding a leading zero, Redux does not recognize it as a change. For example "005" and "5" are both seen as 5. Therefore, the component does not re-render. I've tried a lot of different fixes, but nothing resolves this issue.
Anyone have a recommendation?
OK, I found a fix. Adding to the isAllowed prop ended up being the correct point of intervention. Here's what I ended up with:
<NumberFormat
value = {this.props.input.value}
onFocus = {()=>{}}
onBlur = {this.handleChange.bind(this)}
onChange = {this.handleChange.bind(this)}
onInput = {this.handleChange.bind(this)}
thousandSeparator = {true}
prefix = {this.props.prefix}
suffix = {this.props.suffix}
decimalScale = {1}
isNumericString = {true}
isAllowed={(values) => {
const {value, floatValue} = values;
if (typeof floatValue==='undefined' || typeof value==='undefined') {
return true;
}
if (value.charAt(0)==='0') {
if (value.charAt(1) && value.charAt(1)!='.') {
return false
}
}
if (this.props.maximum) {
return floatValue <= this.props.maximum;
} else {
return true;
}
}}
/>
I think you may use like this:
value={parseInt(this.props.input.value, 10) || 0}
or
value={() => {return parseInt(this.props.input.value, 10)}}
Kind regards

Resources