Trigger onChange event manually - javascript

I would like to call my onChange manually.
Here's my select with onChange (I pass onChangeSelect as a prop from parent component)
return(
<Form Control disabled={disabled}
<NativeSelect ref={ref} onChange={onChangeSelect}>
...
And I'd like to call that onChange every time my variable changes and is empty
useEffect(() => {
if (requiredSelect[0].length > 0 || id === "root1") { setDisabled(false) }
else { ref.current.value = ([[], []]); ref.current.onChange ; setDisabled(true); }
}, [requiredSelect])
And here's onChangeSelect in parent component
<Child onChangeSelect={(e) => rootChange(e)}>
and what it does
const rootChange = e => { setRootSelect(e.target.value.split(',')); }

The simplest solution here would be to change the definition of your rootChange function to accept the value instead of the event itself.
const rootChange = value => { setRootSelect(value.split(',')); }
// In parent:
<Child onChangeSelect={rootChange}>
// Select
<NativeSelect ref={ref} onChange={(e) => onChangeSelect(e.target.value)}>
You can trigger the function manually with:
onChangeSelect(whateverValueYouWant); // notice that you need the brackets when calling the function.
Answer in Typescript
//Child Component
type PropsType = {
onChange: (value: string) => void;
value: string;
};
const CustomInput: FC<PropsType> = (props: PropsType) => {
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
props.onChange(event.target.value);
};
return (<input
onChange={onChange}
type="text"
value={props.value}></input>);
};
//Parent Component
const [input, setInput] = React.useState('');
<CustomInput
onChange={(value: string) => {
setInput(value);
}}
value={input}></CustomInput>

Related

Input focus is not working with specific component react

I have component PhoneConformition which uses input :
<Flexbox>
{CODE_INPUTS.map((input, index) => (
<Controller
key={input.name}
name={input.name}
control={control}
defaultValue=""
// rules={{ required: true }}
render={(props) => (
<InputCode
{...props}
ref={inputListRefs.current[index]}
className={styles.phoneConfirmation__formInput}
onValueSet={() => handleOnValueSet(index)}
/>
)} // props contains: onChange, onBlur and value
/>
))}
</Flexbox>
Where InputCode is =
type Props = {
className?: string;
name: string;
onChange: (...event: any[]) => void;
onValueSet: (value: string) => void;
};
const InputCode = forwardRef<HTMLInputElement, Props>(
({ className, name, onChange, onValueSet }, ref) => {
const classNames = [styles.inputCode, className].join(" ");
const [valueState, setValueState] = useState<string>("");
const handleChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
event.preventDefault();
};
const handleKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
const value: number = parseInt(event.key, 10);
if (!isNaN(value)) {
const valueString: string = value.toString();
setValueState(valueString);
onChange(valueString);
onValueSet(valueString);
}
};
return (
<div className={classNames}>
<input
ref={ref}
name={name}
className={styles.inputCode__input}
type="text"
onChange={handleChange}
onKeyPress={handleKeyPress}
value={valueState}
/>
</div>
);
}
);
export default InputCode;
BUT if i want to use the function on value set function where it focus on another function according to index.Everything is wokring instead of .focus
const handleOnValueSet = (index: number) => {
const formvalues = getValues();
onCodeChange(formvalues);
if (index + 1 < CODE_INPUTS.length) {
inputListRefs.current[index + 1].current?.focus();
}
}
.focus is not working with the onValueSet however with onChange or onKeyDown working correctly.
Found the solution. The issue was that the second onCodeChange call started before the first one finished.
I just added a setTimeOut of 10ms on the second call and it worked.
Thanks all

Custom autocomplete validation in React

I created a custom autocomplete dropdown list, but I need to force the user to select something from the list, keeping at the same time the possibility of typing something in the input field.
How can I create such a validation? Autocomplete component is a list with the possibility to select a thing from the API, but the user still can type anything in the input field and send it as well. I want the user to be able submit the form ONLY with the suggested item. Could you please help me with that one?
Basically the question is: how can I check if the input value is the same as the item taken from the autocomplete component?
export type AutocompletePropsType = {
inputValue: string;
currentIndex: number;
onSelect: (selectedItem: string, slug?: string | null) => void;
hasError: boolean;
list: AutocompleteResponse['data'];
};
type PropsType = {
onChange: (value: string, type: string, isValid?: boolean, slug?: string) => void;
inputName: string;
type: InputType;
label: string;
autocompleteComponent: React.ComponentType<AutocompletePropsType>;
validator?: UseFieldValidatorType;
};
const SearchInput = (props: PropsType): JSX.Element => {
const { onChange, inputName, type, label, autocompleteComponent: AutocompleteComponent, validator } = props;
const autocompleteRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
const classes = useStyles();
const router = useRouter();
const dispatch = useDispatch();
const [currentIdx, setCurrentIdx] = useState(-1);
const [state, { handleInputBlur, handleInputChange, handleInputFocus }] = useField({
inputValidator: validator,
});
const { isFocused, isValid, value } = state;
const [list, { hasError }] = useAutocomplete<AutocompleteResponse['data']>(value, async (query: string) =>
searchApi.autocomplete[type](query).then((res) => (res.data as unknown) as AutocompleteResponse['data']),
);
const handleSelect = (alias: string): void => {
handleInputChange(alias);
onChange(alias, type);
handleInputBlur();
};
const handleFocus = (): void => {
handleInputFocus();
inputRef.current.focus();
};
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
handleInputChange(event);
const isValueValid = validator ? validator(event.target.value).success : true;
onChange(event.target.value, event.target.name, isValueValid);
};
useClickOutside(autocompleteRef, () => {
handleInputBlur();
});
useEffect(() => {
(async (): Promise<void> => {
if (router.pathname !== '/' && !value) {
const { alias, slug } = await getInitialInputValuesFromQuery(router, type);
handleInputChange(alias);
onChange(alias, type, isValid, slug);
}
})();
}, []);
return (
<div className={classes['search-input']} ref={autocompleteRef}>
<div onClick={handleFocus}>
<label
className={cx(classes['search-input__input__label'], {
[classes['search-input__input__label--active']]: isFocused || !!value,
})}
>
{label}
</label>
<input
id={inputName}
onChange={handleChange}
onFocus={handleInputFocus}
name={inputName}
value={value}
className={classes['search-input__input__field']}
autoComplete="off"
checked={false}
ref={inputRef}
maxLength={25}
/>
<div
className={cx(classes['input__decorator'], {
[classes['input__decorator--focused']]: isFocused,
})}
/>
</div>
{transitions(
({ transform, opacity }, item) =>
item && (
<animated.div className={classes['search-input__dropdown']} style={{ opacity: opacity as any, transform }}>
{value.length > 2 && (
<AutocompleteComponent
onSelect={handleSelect}
inputValue={value}
currentIndex={currentIdx}
hasError={hasError}
list={list}
/>
)}
</animated.div>
),
)}
</div>
);
};
export default SearchInput;
Disclaimer: i don't fully read the code because there's a few things in there that I don't understand yet (typescript). But considering this:
Autocomplete component is a list
how can I check if the input value is the same as the item taken from the autocomplete
You can use Array.prototype.includes() to match the user input with your list before sending the form.
let userInput = "foo"
let userInput2 = "opsIAmInvalid"
let apiList = ["foo", "doo", "boo"]
apiList.includes(userInput) // true
apiList.includes(userInput2) // false
Another broad approach could be done with a more complex component. Two ideas:
Two inputs, one is your user text that filters a list, other is the actual item selected from the list. The first one you ignore when sending the form and you can use some focus validation to change styles. (I don't like this idea tho :x)
One input that your user type anything that filters a list. When they clicks in something from the list you save it as variable/state somewhere. You ignore the input on submit, just validates if the variable/state exists.

unable to remove first character from search in input react js

I have made a search field with debouncing. Everything works fine but when I try to empty the search field with backspace it continuously re-show all characters and does not remove them(the first character is always there).
you can see it in the attached gif
my parent component
class ParentComponent extends React.Component {
this.queryParam = {
keyword: ''
}
keywordSearch = value => {
const {
history: { push },
match: { url },
location: { search },
} = this.props;
queryParams = {...queryParams, keyword: value, };
push(`${url}?${queryString})
};
render() {
<SearchComponent
value={this.queryParams.keyword}
onUpdate={this.keywordSearch}
/>
}
}
my search field component
const SearchComponent = ({ value, onUpdate }) => {
const [fieldValue, setFieldValue] = useState(value);
const handleChange = ({ target: { value } }) => {
debounceFunc(() => {
onUpdate(value);
}, 300);
setFieldValue(value);
};
return (
<Input
value={fieldValue || value}
disableUnderline
onChange={handleChange}
className={classes.root}
placeholder='Search'
startAdornment={
<InputAdornment position="start">
<Search className={classes.icon} fontSize="small" />
</InputAdornment>
}
/>
}
here is my custom debounce component
export const debounceFunction = () => {
let timeOut = null;
return (callBack, wait) => {
if (timeOut) clearTimeout(timeOut);
timeOut = setTimeout(() => {
callBack();
}, wait);
};
};
export const debounceFunc = debounceFunction();
the problem is in this debounce function. can anyone help me in this regard? why it isn't removing the first character?
Thanks
First problem is <Input value={fieldValue || value}
=> use just the local state:
<Input value={fieldValue} to change the visible value immediatelly.
Second problem is this.queryParams.keyword being an instance property, not a React State
=> use this.state.... and this.setState(...) (or Hooks) to update debounced state in the parent
I have changed debounceFunction parameter and the way to use it. Can you give it a try
const SearchComponent = ({ value, onUpdate }) => {
const [fieldValue, setFieldValue] = useState(value);
const debouncedUpdate = debounceFunction(onUpdate, 300);
const handleChange = ({ target: { value } }) => {
debouncedUpdate(value);
setFieldValue(value);
};
return (
<Input
value={fieldValue || value}
disableUnderline
onChange={handleChange}
className={classes.root}
placeholder='Search'
startAdornment={
<InputAdornment position="start">
<Search className={classes.icon} fontSize="small" />
</InputAdornment>
}
/>
}
export const debounceFunction = (callBack, wait) => {
let timeOut = null;
return () => {
if (timeOut) clearTimeout(timeOut);
timeOut = setTimeout(() => {
callBack();
}, wait);
};
};
export const debounceFunc = debounceFunction();

update is not capturing and unable to update the input field

please find below code which contains name id and am rendering initially using map
am replacing id value to input type in UI
with the updated input type am trying to update the value onchange
update is not capturing and unable to update the input field
any suggestion?
please refer below snippet
import React, { useState } from "react";
const CstmInput = (props) => {
return (
<input
name={props.name}
type="text"
value={props.value}
onChange={(event) => props.onInputChange(event)}
/>
);
};
export default CstmInput;
import React, { useState } from "react";
import CstmInput from "./CstmInput";
const HierarcyTest = () => {
let rowData = [
{ name: "first", id: 10 },
{ name: "second", id: 20 },
];
const [data, setData] = useState(rowData);
const [name, setName] = useState({ fn: "test" });
const onInputChange = (e) => {
console.log("---event---", e.target.value);
setName({ ...name, fn: e.target.value });
};
let updateValue = () => {
let newData = data.map(
(item, index) =>
(item.id = (
<CstmInput name={item.name} value={item.id} onInputChange={(e) => onInputChange(e)} />
))
);
setData([...data, newData]);
};
return (
<div>
<div>Testing</div>
{data.map((val) => (
<h6>
{" "}
{val.name} {val.id}
</h6>
))}
<button onClick={updateValue}> Click </button>
</div>
);
};
export default HierarcyTest;
A few things why your code isn't working as intended:
1.
let updateValue = () => {
let newData = data.map((item, index) => {
if (item.id === 10) {
return [
(item.id = (
<CstmInput
value={item.id}
onInputChange={(e) => onInputChange(e)}
/>
)),
];
}
});
setData([...data, newData]);
};
In the above function inside the callback of map, you're only returning when a condition satisfies. Are you trying to filter the array instead? If not then return something when the if condition fails.
And why are you returning an array?
return [
(item.id = (
<CstmInput
value={item.id}
onInputChange={(e) => onInputChange(e)}
/>
)),
];
the above code seems logically wrong.
2.
const onInputChange = (e) => {
console.log("---event---", e.target.value);
setName({ ...name, fn: e.target.value });
};
If you want to update state which depends on the previous state then this is how you do it:
setName((prevState) => ({ ...prevState, fn: e.target.value }));
but since you're not actually relying on the properties of the previous state you can just use:
setName({fn: e.target.value });
Note that since your state only has one property and you want to update that single property you can completely overwrite the state, you don't need to spread the previous state.
update
change the updateValue function as the following:
let updateValue = () => {
setData(prevData => {
return prevData.map(el => {
return { ...el, id: <CstmInput value={el.id} onInputChange={(e) => onInputChange(e)} /> };
})
});
};
A stackblitz example I've created that implements what you're trying to do.

input target value from child component

I've managed to get my input component to render onto the dom, however I'm having a bit of trouble accessing the props.
Functional input component
const InputField = props => {
const { inputValue, updateInputValue } = props
return (
<Input>
<input
type="text"
placeholder="Please specify"
value={inputValue}
onChange={updateInputValue}
/>
<hr />
<label>Other</label>
</Input>
)
}
The component is only rendered to the dom based on an object property inside of an array
const MultiChoiceQuestions = props => {
const { multiChoiceArray, handleClick } = props
return (
<ButtonContainer>
{multiChoiceArray.map(questionChoice => {
if (questionChoice.type === 'input') {
return <InputField />
}
return (
<Button type="button" key={questionChoice.id} onClick={() => handleClick(questionChoice)}>
{questionChoice.text}
</Button>
)
})}
</ButtonContainer>
)
}
The multiChoice component is imported once again to create a top-level component that the app consumes
const Question = props => {
let responses
switch (props.data.type) {
case 'multiChoice':
responses = (
<MultiChoiceQuestions
multiChoiceArray={props.data.choices}
handleClick={props.handleClick}
inputValue={props.inputValue}
updateInputValue={props.updateInputValue}
/>
)
break
default:
responses = <div>Error: no question type: `{props.data.type}`</div>
}
const { data } = props
return (
<AnimatedDiv key={data.id}>
<QuestionText>{data.text}</QuestionText>
{responses}
</AnimatedDiv>
)
}
And the final component looks like this
class Survey extends Component {
constructor(props) {
super(props)
this.state = {
currentQuestionId: 1,
userAnswers: [],
isActive: false,
inputValue: '',
}
this.selectAnswer = this.selectAnswer.bind(this)
this.test = this.test.bind(this)
}
selectAnswer = answer => {
this.setState(state => ({
currentQuestionId: state.currentQuestionId + 1,
userAnswers: state.userAnswers.concat([answer]),
isActive: !state.isActive,
}))
}
checkInput = event => {
this.setState({
inputValue: event.target.value,
})
}
test = event => {
console.log(event.target.value)
}
render() {
const { currentQuestionId, isActive, inputValue } = this.state
const { questions } = this.props
const currentPercentage = (currentQuestionId * 100) / questions.length
return (
<SurveyContainer>
<Question
data={questions.find(q => q.id === currentQuestionId)}
className={isActive ? 'active' : ''}
handleClick={this.selectAnswer}
value={inputValue}
onChange={this.test}
/>
</SurveyContainer>
)
}
}
The InputField component renders out just fine, however, the function for my onChange event is not firing...There's a mistake somewhere in the pipeline, probably inside the question component?
It looks like you haven't passed any props to <InputField /> in your MultiChoiceQuestions component.
I can not see where you pass props from
<MultiChoiceQuestions>
...
<InputFiled props={props} />
...
</MultiChoiceQuestions>
Probably pass only the props which are needed in InputField component, such as inputValue, updateInputValue:
<InputFiled
inputValue={inputValue}
updateInputValue={updateInputValue}
/>
const InputField = (inputValue, updateInputValue) => {
...
<input
type="text"
placeholder="Please specify"
value={inputValue}
onChange={(e) => updateInputValue(e)}
/>
...
}
Hope that will help.

Categories

Resources