I set up debounce inside my functional component like this:
const debouncedFunc= debounce(myFunction, 500);
I have the below TextField
<TextField
id="myField"
maxLength={8}
onChange={(e) => debouncedFunc(e.target?.value)}
/>
I have the myFunction like this
function myFunction(val) {
if (val.length === 8) {
console.log(val);
}
}
So this works well. It prints value when a user types eight characters into the field. The problem is that I need to empty the value in this field when a user types eight characters, and debounced function does kick in. Normally, TextField, I can empty the value in the field by e.target.value="". Since I am in the debounce function, I do not have a reference to the e, so I cannot empty it.
Long question short, what is the best way to empty the textfield from a debounce function?
My current and only solution is this, anyone that can think of a better solution please do share
export default function DebounceFeaturedTextfield() {
const debouncedFunc= debounce(myFunction, 500);
let TextFieldRef = "";
function myFunction(val) {
if (val.length === 8) {
TextFieldRef.setInputValue("");
}
}
return (
<TextField
ref={(r) => {
TextFieldRef = r;
}}
id="myField"
maxLength={8}
onChange={(e) => debouncedFunc(e.target?.value)}
/>
);
}
Related
Hi there SO!
I'm currently trying to make a form that generates based on the object supplied and this seem to work at just about anything I throw at it.
That is, until I get to a nested object.
The problem:
Once I hit the if condition (typeof value === "object") I want to have a hidden input (this works).
Then I want to go into that object I just identified, and into all child objects it may contain and generate the input on same criteria as the initial run-through.
function GenericForm(props: any) {
var object = props.object;
return (
<div>
<form>
{Object.entries(object).map(([property, value]) => {
let type: string = "";
if (typeof value === "string") {
type = "text";
} else if (typeof value === "number") {
type = "number";
} else if (typeof value === "boolean") {
type = "checkbox";
} else if (value instanceof Date) {
type = "date";
} else if (typeof value === "object") {
type = "hidden";
}
return [
<label property={property} htmlFor={property}>
{property}
</label>,
<input
type={type}
id={property}
name={property}
defaultValue={value as string}
onChange={(newVal) => {
object[property] = newVal.target.value;
}}
/>,
];
})}
</form>
</div>
);
}
export default GenericForm;
I'm aware that this most likely utilizes some kind of recursion and while I have tried to solve it using recursion, I haven't been able to solve it. the code pasted here is from before I tried recursion, to have a "clean sheet" from where it went wrong for me.
EDIT 1 - added info on object structure
the object passed should be completely generic and allow for objects of any structure to be handed to the component, currently it should then just evaluate what type the properties are and make an input element from that.
one of the current objects I'm passing have the following JSON Schema
{
"id": "XYZ1",
"type": "twilio-api",
"sid": "someSID",
"from": "+phonenumberhere",
"name": "TWILIO SMS",
"credentials": {
"token": "someapitoken"
}
}
above object currently renders like so:
Assuming you have a Input component:
function Input = ({ name, type, value }) => {
// most of your code can fit here
return <input name={name} type={type} value={value} />
}
You can use your version of code, I use a simplified version as above to make our discussion easier. With that we can design a InputList component:
function InputList = ({ object }) => {
console.log('list', object)
return (
<div>
{Object.entries(object).map(([property, value]) => {
if (typeof value === "object") {
return <InputList object={value} />
} else {
return <Input name={property} />
}
})}
</div>
)
}
You can see inside this InputList, there's a call to InputList again, so that is the recursion you are looking for. The recursion stops when you don't have an object inside an object any more.
NOTE: React requires value and onChange to drive any input box. Otherwise they'll just behave like a native input. But this is not part of this question.
I currently have a React tsx page with some input boxes; for example:
<textarea value={this.state.myData!.valueOne}
onChange={(e) => this.handleValueOneChange(e)}/>
<textarea value={this.state.myData!.valueTwo}
onChange={(e) => this.handleValueTwoChange(e)}/>
handleValueOneChange and handleValueTwoChange look almost identical:
handleValueOneChange(event: React.FormEvent<HTMLTextAreaElement>) {
let newState = { ...this.state };
newState.myData!.valueOne = event.currentTarget.value;
this.setState(newState);
}
handleValueTwoChange(event: React.FormEvent<HTMLTextAreaElement>) {
let newState = { ...this.state };
newState.myData!.valueTwo = event.currentTarget.value;
this.setState(newState);
}
Is it possible to have a single function for both events; for example:
handleDataChange(event: React.FormEvent<HTMLTextAreaElement>, valueByRef) {
let newState = { ...this.state };
valueByRef = event.currentTarget.value;
this.setState(newState);
}
I'm a little unclear on what this might look like in TypeScript: is what I'm trying to do possible and, if so, how?
EDIT:
To further complicate matters myData contains a mix of types (which appears to be an issue for TS)
Try something like this
HTML becomes
<textarea value={this.state.myData!.valueOne}
onChange={(e) => this.handleValueChange(e, 'valueOne')}/>
<textarea value={this.state.myData!.valueTwo}
onChange={(e) => this.handleValueChange(e, 'valueTwo')}/>
and logic (I suppose your state has type MyState)
handleDataChange<K extends keyof MyDataType>(event: React.FormEvent<HTMLTextAreaElement>, ref: K) {
let newState = { ...this.state };
newState.myData![ref] = event.currentTarget.value;
this.setState(newState);
}
EDIT:
I fixed the answer according to the comments
EDIT:
If myData contains strings, numbers and booleans you can use type guards and casts
handleDataChange<K extends keyof MyDataType>(event: React.FormEvent<HTMLTextAreaElement>, ref: K) {
let newState = { ...this.state };
let old = newState.myData![ref];
if (typeof old === "number") newState.myData![ref] = parseInt(event.currentTarget.value); /* or parseFloat, this depends on your app */
else if (typeof old === "boolean") newState.myData![ref] = (event.currentTarget.value === "1"); /* use any string -> boolean conversion function */
else newState.myData![ref] = event.currentTarget.value; /* string case */
this.setState(newState);
}
Of course, pass a string back, for example:
<Input onChange={(event)=>this.handleChange(event, 'type1')} />
If you give your text area's a name prop, you can use this name to add the value to state.
handleChange(event: React.FormEvent<HTMLTextAreaElement) {
this.setState({
...this.state.myData,
[event.currentTarget.name]: event.currentTarget.value,
})
}
<textarea value={this.state.myData.one} name="one"
onChange={this.handleChange} />
<textarea value={this.state.myData.two} name="two"
onChange={this.handleChange} />
onChange prop looks cleaner in this case too. No use of an inline arrow function.
Working Code sandbox https://codesandbox.io/s/04rm89oq8w
This is the function I want to invoke in an input type:
_handleOnEnterPress = (e, receiverUserId) => {
if (e.keyCode === 13) { // guess keycode 13 is enter?
console.log("pressed enter. user id = ",receiverUserId)
}
};
and this is the input type I want to invoke when ENTER is pressed, I used onKeydown
<input className="popup-input"
onKeyDown={this._handleOnEnterPress(chatFriendPopPup.id)}
onChange={this._handleMessageTextChange}
type='text'
/>
when I press enter, it's not logging on the console. I used e(event) default arg so it can detect the keycode 13 (enter) but I guess the if fails? since the receiverUserId is on 2nd index, do I not have to pass e (event) arg on onKeyDown when the function is invoked?
Have you tried onKeyDown={(e) => this._handleOnEnterPress(e, chatFriendPopPup.id)} ?
Try using an arrow function to catch the event and then pass that event object as well any argument you like to the handler method of your choosing.
handler(e, someInput) {
// do something in here
if (someInput === 'foo') console.log('bar');
}
render() {
const someArg = 'foo';
return (
<button onClick={e => this.handler(e, someArg)}>Do something</button>
)
}
I'm trying to manipulate the value of an HTML5 number input's value to equal min if a user keys in a value less than min. However, the function fails because my two parseFloats return NaN (and they definitely shouldn't be--I can access both arguments in the console when I use a debugger).
componentDidMount() {
const autocorrect = this.props.autoCorrect; // true
this.inputNode.addEventListener('blur', (e) => {
const value = parseFloat(this.value); // NaN ?
const minimum = parseFloat(this.min); // NaN ?
if (this.props.autoCorrect && (value < minimum)) {
this.value = minimum;
}
});
}
render() {
return (
<input
ref={(input) => {
this.inputNode = input;
}}
type='number'
min={this.props.min}
value={this.props.value}
/>
);
}
In fact, if I stop the function with a debugger, I can force the input's value to change using this.value = 23, for example. So what am I overlooking here in my function?
Arrow functions do not define their own context - this in your (e) => {...} is the same as the conext in which componentDidMount was called.
if I want to handle input of character *, I can use handleBeforeInput(str):
handleBeforeInput(str) {
if (str !== '*') {
return false;
}
// handling
return true;
}
if I want to handle input of ENTER, I can use the hook handleReturn(e)
but if I want to handle input of DELETE, how to do?
Draft's Editor component takes an optional prop called keyBindingFn. If you assign a function to it, that function will receive all keyDown events. In theory, you could do whatever you want in this function, but its responsibility is really to return a command, of type string, that should be executed for a specific key (or combination of keys). It could look something like this:
function keyBindingFn(e) {
if (e.key === 'Delete') {
return 'delete-me' // name this whatever you want
}
// This wasn't the delete key, so we return Draft's default command for this key
return Draft.getDefaultKeyBinding(e)
}
The Editor component also takes another optional prop called handleKeyCommand. If a function is assigned to this, it will receive all commands executed in the editor. This means that it, if you used my example above, would receive the command 'delete-me', whenever the delete key is pressed. This is the place to handle that command.
function handleKeyCommand(command) {
if (command === 'delete-me') {
// Do what you want to here, then tell Draft that we've taken care of this command
return 'handled'
}
// This wasn't the 'delete-me' command, so we want Draft to handle it instead.
// We do this by telling Draft we haven't handled it.
return 'not-handled'
}
To clarify, you pass these functions to the Editor component like this:
<Editor
keyBindingFn={keyBindingFn}
handleKeyCommand={handleKeyCommand}
... // other props
/>
You can read more about it in the Draft docs.
The way to do it in draft-js version ^0.11.7 is:
import Editor, {getDefaultKeyBinding, KeyBindingUtil} from 'draft-js';
const {hasCommandModifier} = KeyBindingUtil;
class MyEditor extends React.Component {
constructor(props) {
super(props);
this.handleKeyCommand = this.handleKeyCommand.bind(this);
}
// ...
handleKeyCommand(command: string): DraftHandleValue {
if (command === 'enter_command') {
console.log('enter_command');
return 'handled';
}
if (command === 'ctrl_s_command') {
console.log('ctrl_s_command');
return 'handled';
}
return 'not-handled';
}
myKeyBindingFn = (e) => {
if (e.keyCode === 13 /* `enter` key */ ) {
return 'enter_command';
}
if (e.keyCode === 83 /* `S` key */ && hasCommandModifier(e) /* + `Ctrl` key */) {
return 'ctrl_s_command';
}
//else...
return getDefaultKeyBinding(e);
}
render() {
return (
<Editor
editorState={this.state.editorState}
handleKeyCommand={this.handleKeyCommand}
keyBindingFn={myKeyBindingFn}
...
/>
);
}
}
You can detect Delete key using JavaScript's keydown event as follows:
var input_field = document.getElementById('your_text_field');
input_field.addEventListener('keydown', function () {
if (event.keyCode == 46) { //Here 46 is key-code of "Delete" key
//...your work when delete key pressed..
}
});
Hope, you needed this.