Reputation: 135
I have an input field with a "close (x)" icon next to it. I am handling state as the value in the input field changes, and once the user hits Enter I clear the value in the state. However, after the re-rendering (because of state change) the previous input still remains there in the input field.
constructor(props) {
super(props);
this.state = {
filterSearchQuery: null, filterSearch: null}
this.handleSearchInput = this.handleSearchInput.bind(this);
this.handleSearchReset = this.handleSearchReset.bind(this);
}
handleSearchInput(e) {
// e.preventDefault();
let val = e.target.value;
if (e.keyCode == 13)
this.setState({ entries: [], filterSearchQuery: null, filterSearch: val, pageIndex: 0, hasMore: false }, this.loadEntries);
else
this.setState({ filterSearchQuery: val });
}
handleSearchReset(e) {
//e.preventDefault();
this.setState({ entries: [], pageIndex: 0, hasMore: false, filterSearchQuery: null, filterSearch: null }, this.loadEntries);
}
render and return :
<input type="text" className="outline-none flex-fill pt-0_2em pb-0_2em border-white border-none bg-color-white color-black font-12px font-weight-bold opacity-0_9" value={this.state.filterSearchQuery} placeholder="Search in results." onKeyUp={(e) => this.handleSearchInput(e)} onChange={(e) => this.handleSearchInput(e)} autoFocus={true} />
{
(this.state.filterSearchQuery && this.state.filterSearchQuery.length > 0) &&
<div className="">
<button type="button" className="close" aria-label="Close" onClick={this.handleSearchReset}>
<span aria-hidden="true">×</span>
</button>
</div>
}
The handleSearchReset
function works and sets the state as expected. Both the filterSearch
and filterSearchQuery
values are set properly. However, after re-rendering the filterSearchQuery
value still is visible in the input field. How can I overcome this or where am I missing?
Upvotes: 3
Views: 1969
Reputation: 56925
Since you're using controlled components, simply setting them to null
isn't sufficient to perform a clear. Instead, set them to the empty string ""
. Here's a minimal, complete example:
class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
filterSearchQuery: "",
filterSearch: ""
};
this.handleSearchInput = this.handleSearchInput.bind(this);
this.handleSearchReset = this.handleSearchReset.bind(this);
}
handleSearchInput(e) {
if (e.key === "Enter") {
this.setState(prevState => ({
filterSearchQuery: "",
filterSearch: prevState.filterSearchQuery,
}));
}
else {
const {value} = e.target;
this.setState(prevState => ({
...prevState,
filterSearchQuery: value,
}));
}
}
handleSearchReset(e) {
this.setState(prevState => ({
filterSearchQuery: "",
filterSearch: "",
}));
}
render() {
const {filterSearchQuery, filterSearch} = this.state;
return (
<div>
<input
value={filterSearchQuery}
onKeyUp={this.handleSearchInput}
onChange={this.handleSearchInput}
/>
{filterSearchQuery &&
<div>
<button onClick={this.handleSearchReset}>
<span>×</span>
</button>
</div>
}
{filterSearch &&
<div>
...testing search for '{filterSearch}'...
</div>
}
</div>
);
}
}
ReactDOM.createRoot(document.querySelector("#app"))
.render(<Search />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
Hooks version:
const Search = () => {
const [
filterSearchQuery,
setFilterSearchQuery
] = React.useState("");
const [filterSearch, setFilterSearch] = React.useState("");
const handleSearchInput = e => {
if (e.key === "Enter") {
setFilterSearch(filterSearchQuery);
setFilterSearchQuery("");
}
else {
const {value} = e.target;
setFilterSearchQuery(value);
}
};
const handleSearchReset = e => {
setFilterSearch("");
setFilterSearchQuery("");
};
return (
<div>
<input
value={filterSearchQuery}
onKeyUp={handleSearchInput}
onChange={handleSearchInput}
/>
{filterSearchQuery &&
<div>
<button onClick={handleSearchReset}>
<span>×</span>
</button>
</div>
}
{filterSearch &&
<div>
...testing search for '{filterSearch}'...
</div>
}
</div>
);
};
ReactDOM.createRoot(document.querySelector("#app"))
.render(<Search />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
Minor remarks:
event.keyCode
is deprecated. Use .key
instead.this
to this.handleSearchFoo
in the constructor, there's no need for an arrow function wrapper in render()
.this.state.filterSearchQuery && this.state.filterSearchQuery.length > 0
can be simply this.state.filterSearchQuery
because unlike empty arrays, empty strings are falsey.===
rather than ==
in JS.Upvotes: 4