(React.js + Hooks) How to reset input field after button click? - javascript

What I want to do is have an input that a user can type into. Upon clicking a button, take that input and process it and perform some backend operations with the database connected to the input. I'm trying to accomplish this with a state hook: onBlur, save input into state. On button click, do two things: 1) take the state variable and pass it to the backend resolvers, and 2) clear the input field so it's empty, and only the placeholder text exists.
I have this code:
const [inputVal, setInputVal] = useState("");
updateInput = (e) => {
const val = e.target.value;
setInputVal(val);
}
sendData = async () => {
//handle async backend processing with inputVal
setInputVal("");
//rerender
}
<Button className="input-button" onClick={sendData}>Send Data</Button>
<input className="input-field" placeHolder="Input name." defaultValue={inputVal} onBlur=(updateInput)></input>
However, I have two issues.
On button click, I want to first updateInput, and then handle the button click, so it always uses the new value. However, it seems as if there are some issues with that, possibly due to the asynchronous nature of sendData?
While inputVal may be an empty String, for some reason, the value in the input box doesn't reset to nothing, it stays exactly as it was (although the internal data would still have inputVal = 0). And onBlur it reupdates the inputVal.

for a controlled state input the most common approach is to use onChange rather than onBlur. This would also avoid your conflicts with blur and click events. Also you would pass inputVal to value input's property.
const [inputVal, setInputVal] = useState("");
const updateInput = (e) => {
const val = e.target.value;
setInputVal(val);
}
const sendData = async () => {
//handle async backend processing with inputVal
setInputVal("");
//rerender
}
return (
<>
<button className="input-button" onClick={sendData}>Send Data</button>
<input className="input-field" onChange={updateInput} placeHolder="Input name." value={inputVal}/>
</>
);

First, change defaultValue to value={inputVal} since it's a controlled input.
Secondly, please elaborate on what issues you have in 1.

Related

how to change element function with js

i have a <input type="text"/> which is to perform two functions. Which function is executed is to be decided via a button. This button should toggle the onkeyup function. For this I used the following:
document.getElementById('input-search-tags').onkeyup = filter_tagsC()
but when I click the button, the function will not change.
To toggle between which function fires onkeyup you need a click event listener on the button to set which function needs to be run.
const input = document.querySelector("input")
const button = document.querySelector("button")
let functionState = "one"
const oneFunc = () => console.log("one")
const twoFunc = () => console.log("two")
input.onkeyup = oneFunc
const toggleFunctions = () => {
if (functionState === "one") {
input.onkeyup = twoFunc
functionState = "two"
} else {
input.onkeyup = oneFunc
functionState = "one"
}
}
button.addEventListener("click", toggleFunctions)
<div>
<input type="text" />
<button>Toggle function</button
</div>
With this example, I get both elements with querySelector. Then I set a state value with let so that its value can be changed. Then I define the 2 functions I want to toggle between. I also set the input to have onkeyup of oneFunc, since the functionState starts as one.
Then I define a function that will assign a different function to onkeyup depending on the state of functionState and reset the functionState to the new value. Lastly, I add an event listener to the button to run the toggleFunctions function on click.
You need to specify that you are calling a function onKeyUp and then specify that function's name:
document.getElementById('input-search-tags').onkeyup = function() {filter_tagsC()}
function filter_tagsC() {
// Your Code Here.
}
For an an easier method of handling onkeyup, I would suggest looking at the keyup event listener method, as it is a bit more logical way of handling events. Hope this helps.

How to make onChange only fire once value is changed in React

I'm using a range input in my React application. I would like to log the value selected once the range slider has changed (not while it is changing). Here is an example of my issue:
const App = () => {
const handleChange = (e) => {
console.log(e.target.valueAsNumber);
}
return <input type="range" onChange={handleChange}/>
}
ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
If you view the browser console, the value is being logged as you slide the range slider. My desired output is so that the handleChange callback is only fired once you release the slider. I would like it to behave in a similar way that onChange behaves in vanilla HTML:
const handleChange = (e) => {
console.log(e.target.valueAsNumber);
}
<input type="range" onChange="handleChange(event)"/>
It seems like react has made onChange behave like onInput, which in my case is undesirable. Is there a simple prop that I'm missing to make this work? Or do I need to use refs or some other method to make this work as intentded?
The behaviour you want is of onchange event listener. Unfortunately, React connects onChange prop to oninput event handler.
There's also an active issue about this: https://github.com/facebook/react/issues/3964 Document how React's onChange relates to onInput #3964
Simple fix would be to use onchange event listener using ref.
const NewInput = (props) => {
const setRef = useCallback((e) => {
e.target.onchange = props.onChange || null;
}, [props.onChange]);
return <input ref={setRef} {...props} />
}
Another approach is discussed here: In React, what's the difference between onChange and onInput?

How to simulate text input onchange when testing

I have the following code where I am only enabling a button if relevant data exists.
I am trying to test this by faking a change in the input field so that the relevant data is available.
But my simulation does not seem to work thus the data remains empty which in turn means my button remains disabled. Can I please get some help on what I am doing wrong please? Thank you.
Note that following code works where events fires as expected and updates. Just issue in writing test.
const [code1, setCode1] = useState('');
const cannotPress = () => !(code1);
const handleFieldChange = (event, updater) => {
updater(event.target.value);
};
// in test, trying to simulate a value entered in this input so that the event fires and
// updates code1's value thus making cannotPress=false. This would enable the button and pass test.
<input id="code1" value={code1} onChange={event => handleFieldChange(event, setCode1)} />
<button id='btn type="submit" disabled={cannotPress()} onClick={() => doSomething()}>Submit</button>
Test
describe('tests', () => {
const wrapper = shallow(<MyApp info={info} />);
it('simple test', () => {
const btn = wrapper.find('button#btn'); // able to find button
const input = wrapper.find('input#code1'); // able to find input (tested by placing in default values)
console.log(input.props().value); // prints nothing as expected
input.instance().props.onChange(({ target: { value: 'some fake data' } })); // expecting the event to trigger and change value for code1.
console.log(input.props().value); // ISSUE: expected to print 'some fake data' but still empty.
expect(btn.prop('disabled')).toBe(false); // fails cos input has no value thus disabled is still true
});
});
I think you will need to simulate the event (using enzyme) rather then trying to manipulate the onChange() property directly.
Seems like a similar issue to:
https://stackoverflow.com/a/37452043/10610784
Try the suggested solution
input.simulate('change', { target: { value: 'Hello' } })

form.submit callback not getting invoked

I'm using the react-bootstrap-typeahead module in one of my application. This is working fine, except in one case.
I'm not able to submit the form by pressing the ENTER key if there are no results.
ie;
if there are suggestions provided by react-bootstrap-typeahead, I'm able to select one of the options and submit the form. In this case, able to invoke the callback onSubmit.
if there are no suggestions provided by react-bootstrap-typeahead, not able to submit the form.
If I submit the form using form.submit() method onKeyDown event, the form will be submitted, however, the page gets refreshed instead of invoking callback, which results in complete out of my control result.
The desired result: I should be able to invoke onSubmit callback even if there is no suggestion provided by if there are suggestions provided by react-bootstrap-typeahead.
Here is my code.
<form ref={(form) => this.form = form} onSubmit={this.sendMessage}>
<Typeahead
id="rbt-example"
dropup={true}
ref={(typeahead) => this.typeahead = typeahead}
onChange={this.valueChanged}
onInputChange={this.updateQuery}
onBlur={(e) => this.updateQuery(e.target.value, e)}
onKeyDown={(e) => {
// Submit the form when the user hits enter.
if (e.keyCode === 13) {
this.form.submit();
}
}}
options={options}
placeholder="Type your queries here..."
renderMenu={(results, menuProps) => {
// Hide the menu when there are no results.
if (!results.length) {
return null;
}
return <TypeaheadMenu {...menuProps} options={results} />;
}}
/>
<button type="submit">Submit</button>
</form>
The issue is likely calling this.form.submit(), which handles the form submission in the DOM (instead of React), and as you say, takes it out of your control. It's refreshing the page because you don't have control over the event to call event.preventDefault().
Instead of this.form.submit, you should call this.sendMessage when the user presses enter. Presumably you're calling event.preventDefault in sendMessage, so you should pass the event through from onKeyDown:
onKeyDown={e => {
if (e.keyCode === 13) {
this.sendMessage(e);
}
}}
This way, you will be handling form submission the same whether in response to the user pressing the submit button or enter.
If you noticed the code in my question, I'm handling multiple events. Especially onChange and onKeyDown.
Couple of things we need to understand about react-bootstrap-typeahead is
onChange, react-bootstrap-typeahead will pass the selected object to callback whereas onKeyDown react-bootstrap-typeahead will pass the event, from which, I will get the value using event.target.value
onChange will be triggered only after onKeyDown. therefore, if we want to do some operation based on the selected object and that value to be used in onKeyDown callback will not work.
To overcome this situation, I used setTimeout also removed form element.
so my solution simply becomes
<Typeahead
id="rbt-example"
dropup={true}
ref={(typeahead) => this.typeahead = typeahead}
onChange={this.valueChanged}
onInputChange={this.updateQuery}
onBlur={(e) => this.updateQuery(e.target.value, e)}
onKeyDown={(e) => {
// Submit the form when the user hits enter.
if (e.keyCode === 13) {
if (this.timerid) {
clearTimeout(this.timerid);
}
this.timerid = setTimeout(
() => {
this.sendMessage();
},
300
);
}
}}
options={options}
placeholder="Type your queries here..."
renderMenu={(results, menuProps) => {
// Hide the menu when there are no results.
if (!results.length) {
return null;
}
return <TypeaheadMenu {...menuProps} options={results} />;
}}
/>
<button onClick={() => this.sendMessage() }>Send</button>
This way, I'm calling sendMessage method onKeyDown and on button click. I'm also able to make use of the selected option object.

Initializing then updating textarea value in redux app.

Inside my render function I have
<textarea value={ this.props.song.client_lyrics }
onKeyUp={ (event) => this.props.editLyrics(event.target.value) } >
</textarea>
Normally the editLyrics function will dispatch an action which updates the client_lyrics. But since I have value = { this.props.song.client_lyrics } instead of event.target.value sending the initial value + what I typed, it only keeps sending over the original client_lyrics which just keeps setting client_lyrics to itself.
How can I initialize the textarea with a value and then append more characters to it so the client_lyrics prop actually changes?
Make use of an onChange instead of an onKeyUp event on the textArea
<textarea value={ this.props.song.client_lyrics }
onChange={ (event) => this.props.editLyrics(event.target.value) } >
</textarea>

Categories

Resources