I am new using Fresh by Deno, and I am trying to update the state of a Preact component while typing text into an input.
export default function Calculous(props: CalculousProps) {
const [input, setInput] = useState<string>('');
const onSubmit = (event: h.JSX.TargetedEvent<HTMLFormElement>) => {
event.preventDefault();
console.log(input);
}
useEffect(() => {
console.log(input);
}, [input])
return (
<form onSubmit={onSubmit}>
<input type="text" value={input} onChange={(e) => setInput(e.target.value)} />
<button disabled={isNaN(Number(input))} type="submit">Next</button>
</form>
);
}
However, when I type text on my component, it doesn't re-render until I unfocus the input. Do you know how to trigger re-rendering to update my state ?
Thanks for your help
You should generally use onInput instead of onChange when using Preact with an input element.
React chose to use the name onChange for the event listener callback property for both the change and input events. Preact's API is more semantic and better aligned with the spec in this case.
Related
I have a very simple react component with input:
const Search = ({ onchange, t }) => {
const [value, setValue] = useState('');
useEffect(() => {
console.log('value', value);
}, [value]);
return (
<div className={'user-search'}>
<input
placeholder={t('placeholder')}
className={'search-input'}
onChange={(e) => setValue(e.target.value)}
type="text"
/>
<div className={'search-icon'}>
<icon className={'icon'} size={'normal'} name="search" />
</div>
</div>
);
};
At the same time, I use a library with client components - most of them create a shadow-root in the DOM. Nevertheless, I can drop my components inside them via {children} or sometimes props.
Well, and I have a problem with this Search component: when I use it loosely anywhere in the project I get a console.log with "value" after typing something in the input. On the other hand, when I put it into the component with modal and accordion (from the client library) - input completely stops working and doesn't respond.
Could the non-functioning input have something to do with shadow-dom? Or do I need to provide more information?
React Hook Forms detect that I change the value of text input when I type something (onChange). But can it also detect the change if I enter data to the input by value={value}?
const validator = register(name);
I need something like
onValueSet={(e) => {
validator.onChange(e);
}}
I mean if I just set data by value={value} I get an error that this input is required. I don't want this error, because data was set by value={value}.
Here is my input:
<StyledInput
name={name}
maxLength={100}
value={value}
onChange={(e) => {
validator.onChange(e);
}}
/>
and my validation schema:
const validationSchema = Yup.object().shape({
first_name: Yup.string()
.required("required"),
});
You have to use reset here and call it when you received your initial form data. I assume you're doing an api call and want to set the result to the form. You can do so with useEffect, watching when you're data has resolved and then reset the form with the actual values. This way you don't have to set the value via the value prop and let RHF manage the state of your inputs. You also don't need to set name prop, as RHF's register call returns this prop as well.
const Component = (props) => {
const { result } = useApiCall();
const { register, reset } = useForm();
useEffect(() => {
reset(result)
}, [result]);
return (
...
<StyledInput
{...register('first_name')}
maxLength={100}
/>
...
)
}
Here is a little CodeSandbox demonstrating your use case:
You can define an onfocus or onkeyup event and call the validation function there instead of onChange event.
<StyledInput
name={name}
maxLength={100}
value={value}
onfocus={(e) => {
validator.onChange(e);
}}
/>
Instead of triggering the validator when input changes, you can instead call your validator through the onBlur event. And I am not sure how you are using react-hook-form .. but the useForm hook has a config mode (onChange | onBlur | onSubmit | onTouched | all = 'onSubmit') on when to trigger the validation:
So I'm trying to set the default value of input tag to a state but when I do so I'm unable to change anything in the input tag, The text just gets stuck, Here is an example of my code:
function RandomScreen(){
const [State, SetState] = useState()
// Make API call to set the state
const inputValue = State
return(
<input onChange={handleState} value={inputValue} />
)
}
I'm using onChange but did not want to include it to reduce noise and to minimize code.
You need onChange function to change value of input
Try something like below:-
function RandomScreen(){
const [inputState, setInputState] = useState('');
return(
<input value={inputState} onChange={(e) => setInputState(e.target.value)}/>
)
}
You are not seeing the changes in Input because you are making the input controlled and not updating the state.
Try doing this:
function RandomScreen(){
const [input, setInput] = useState();
const onChange = e => {
// call API here
setInput(e.target.value)
}
return(
<input value={input} onChange={onChange} />
)
}
Read more about controlled components here:https://reactjs.org/docs/forms.html#controlled-components
For making API call you can also take use of useEffect hook.
You have to write an event handler called onChange which will update the state as you type. Here is a slightly changed and updated code -
function RandomScreen(){
const [inputValue, setInputValue] = useState('')
// Make API call to set the state
return(
<input value={inputValue} onChange={(event) => setInputValue(event.target.value)} />
)
}
I have a list and edit button when user click edit button opening a new modal. I want to auto populate the selected username mail etc. Server side response is {this.test.name} i give him to input value to auto populate but when user click edit button ı can see the name but ı couldnt change the input how do ı do that ?
Code :
<div className = "form__group field">
<input type="input" className="form__field" placeholder="Name" name="name" id="name" value={this.test.name} />
<label htmlFor="name" className="form__label">Adı-Soyadı</label>
</div>
You cannot change that input value because you've set the value to this.test.name and did not defined an onChange handler. So what you should do is create a state for your input field and on componentDidMount, populate this state with data from server.
Then on your input field
this.state = {
val: ''
}
<input value={this.test.name} onChange={e => setState({val: e.target.value})}/>
Maybe there can be a syntax error, because I am used to work with hooks now, but that's pretty much the gist of it
Keep the name in the state:
this.state = {
name: '',
}
(setState when you have it, if you retrieve it only on mount)
Pass value={this.state.name} to the input.
Pass onChange={handleNameChange} to the input.
const handeNameChange = (e) => {
setState({ name: e.target.value });
}
If you are using hooks, you could do something like this:
import { useState } from "react"; // import useState
export default function App() {
const [name, setName] = useState(""); // useState hook
// handle change event
const handleChange = (e) => {
e.preventDefault(); // prevent the default action
setName(e.target.value); // set name to e.target.value (event)
};
// render
return (
<div>
<input value={name} type="text" onChange={handleChange}></input>
<p>{name}</p>
</div>
);
}
First, we import the useState() hook to be able to use it, then we use it to store the state for the name value, you can give it an initial value of an empty string (""). In the handleChange() function, we prevent the default action and set the state to e.target.value, this is the input value passed by the event (as (e)). Every time the input changes, the state will update and the page will re-render.
You could check out my sandbox here
Here's a react input component:
function Input({ value, setValue }) {
return (
<div>
<input value={value} onChange={event => setValue(event.target.value)} />
<button onClick={() => setValue(value.toUpperCase())}>Capitalize</button>
</div>
);
}
It's just a vanilla input component together with a button that capitalizes the input's value. It's meant to be controlled by some parent component:
function Parent() {
let [value, setValue] = useState("");
return <Input value={value} setValue={setValue} />;
}
This works fine, but it's not idiomatic. To be useable as a "drop-in replacement" for a vanilla input component, it should take an onChange prop, not setValue, the relevant difference being that onChange takes a synthetic event as an argument while setValue takes a string. (I'd like the presence of the capitalize button to be "opaque" to a developer using this Input component.)
I tried to idiomaticize this (see snippet below) by having the input element fire off a change event when the button is clicked, but the this doesn't cause onChange to execute. (I assume that this is due to details of react's synthetic event system that I don't understand. I browsed a bunch of posts on this topic, but couldn't get the ideas I found to work.)
function AnotherInput({ value, onChange }) {
let input = useRef();
let handleClick = () => {
input.current.value = value.toUpperCase();
var event = new Event("change" /* also tried "input" */, {
bubbles: true
});
input.current.dispatchEvent(event); // onChange doesn't fire!
};
return (
<div>
<input value={value} ref={input} onChange={onChange} />
<button onClick={handleClick}>Capitalize</button>
</div>
);
}
Also, I feel I shouldn't have to use a ref here because I don't want to modify the DOM directly; I just want to change the value in the controlling parent component.
Here's a CodePen.
I made it work by simulating the event Object on the Capitalize Button.
Parent Component:
function Parent() {
let [value, setValue] = useState("");
return <Input value={value} onChange={(e) => setValue(e.target.value)} />;
}
Input Component:
EDITED: I've managed to came up with a more elegant solution to the Input Component:
function Input({ value, onChange: inheritedOnChange }) {
return (
<div>
<input value={value} onChange={inheritedOnChange} />
<button value={value.toUpperCase()} onClick={inheritedOnChange}>Capitalize</button>
</div>
);
}
Note that i renamed the onChange prop to inheritedOnChange just for readability purposes. Preserving the onChange name at the destructuring should still work.