I’m currently struggling with react inputs and debounce from lodash.
Most of the time when I have a form I also have an edit option, so I need a controlled component to fill back the inputs using value={state["targetValue"]}
so I can fill and edit the field.
However, if the component is controlled debounce isn’t working. I made a simple example on CodeSandbox: https://codesandbox.io/embed/icy-cloud-ydzj2?fontsize=14&hidenavigation=1&theme=dark
Code:
JavaScript
x
101
101
1
import React, { Component } from "react";
2
import ReactDOM from "react-dom";
3
import { debounce } from "lodash";
4
5
import "./styles.css";
6
7
class App extends Component {
8
constructor(props) {
9
super(props);
10
this.state = {
11
name: "",
12
title: "",
13
editMode: false
14
};
15
this.debouncedEvent = React.createRef();
16
}
17
18
debounceEvent(_fn, timer = 500, options = null) {
19
this.debouncedEvent.current = debounce(_fn, timer, options);
20
return e => {
21
e.persist();
22
return this.debouncedEvent.current(e);
23
};
24
}
25
26
componentWillUnmount() {
27
this.debouncedEvent.current.cancel();
28
}
29
30
onChangeValue = event => {
31
const { name, value } = event.target;
32
33
this.setState(() => {
34
return { [name]: value };
35
});
36
};
37
38
onRequestEdit = () => {
39
this.setState({ name: "Abla blabla bla", editMode: true });
40
};
41
42
onCancelEdit = () => {
43
if (this.state.editMode) this.setState({ name: "", editMode: false });
44
};
45
46
onSubmit = event => {
47
event.preventDefault();
48
console.log("Submiting", this.state.name);
49
};
50
51
render() {
52
const { name, editMode } = this.state;
53
const isSubmitOrEditLabel = editMode ? `Edit` : "Submit";
54
console.log("rendering", name);
55
return (
56
<div className="App">
57
<h1> How to debounce controlled input ?</h1>
58
<button type="button" onClick={this.onRequestEdit}>
59
Fill with dummy data
60
</button>
61
<button type="button" onClick={this.onCancelEdit}>
62
Cancel Edit Mode
63
</button>
64
<div style={{ marginTop: "25px" }}>
65
<label>
66
Controlled / Can be used for editing but not with debounce
67
</label>
68
<form onSubmit={this.onSubmit}>
69
<input
70
required
71
type="text"
72
name="name"
73
value={name}
74
placeholder="type something"
75
// onChange={this.onChangeValue}
76
onChange={this.debounceEvent(this.onChangeValue)}
77
/>
78
<button type="submit">{isSubmitOrEditLabel}</button>
79
</form>
80
</div>
81
<div style={{ marginTop: "25px" }}>
82
<label> Uncontrolled / Can't be used for editing </label>
83
<form onSubmit={this.onSubmit}>
84
<input
85
required
86
type="text"
87
name="name"
88
placeholder="type something"
89
onChange={this.debounceEvent(this.onChangeValue)}
90
/>
91
<button type="submit">{isSubmitOrEditLabel}</button>
92
</form>
93
</div>
94
</div>
95
);
96
}
97
}
98
99
const rootElement = document.getElementById("root");
100
ReactDOM.render(<App />, rootElement);
101
Advertisement
Answer
So… Apparently, there’s no solution. the input takes the value from the state. While debounce prevents the state to trigger.
I made a workaround using ReactDOM.
JavaScript
1
19
19
1
import ReactDOM from "react-dom";
2
3
export const setFormDefaultValue = (obj, ref) => {
4
if (ref && !ref.current) return;
5
6
if (!obj || !obj instanceof Object) return;
7
8
const _this = [
9
ReactDOM.findDOMNode(ref.current).getElementsByClassName("form-control")
10
];
11
12
if (_this.length > 0) {
13
_this.forEach(el => {
14
if (el.name in obj) el.value = obj[el.name];
15
else console.error(`Object value for ${el.name} is missing...`);
16
});
17
}
18
};
19
and then the use:
JavaScript
1
3
1
this.refForm = React.createRef();
2
setFormDefaultValue(this.state, refForm)
3
This way I can fill my form with the state default value and continue using debounce.