Reputation: 139
I'm trying to filter an array with fetched list of users. Users are stored in component state. I want to filter it by text from input.
Problem: When I enter letters the list is filtering, but when I delete letters result remains unchanged.
Thanks for help!!!
class App extends React.Component {
state = {
isLoading: true,
users: [],
error: null
}
fetchUsers() {
fetch(`https://jsonplaceholder.typicode.com/users`)
.then(response => response.json())
.then(data =>
this.setState({
users: data,
isLoading: false,
})
)
.catch(error => this.setState({ error, isLoading: false }));
}
componentDidMount() {
this.fetchUsers();
}
onChangeHandler(e) {
console.log(e.target.value);
let newArray = this.state.users.filter((d)=>{
let searchValue = d.name.toLowerCase();
return searchValue.indexOf(e.target.value) !== -1;
});
console.log(newArray)
this.setState({
users:newArray
})
}
render() {
const {isLoading, users, error} = this.state;
return (
<div>
<h1>Users List</h1>
<input type="text" value={this.state.value} placeholder="Search by user name..." onChange={this.onChangeHandler.bind(this)}/>
{error ? <p>{error.message}</p> : null}
<ol>
{!isLoading ? (
users.map(user => {
const {username, name, id} = user;
return (
<li key={id}>
<p>{name} <span>@{username}</span></p>
</li>
);
})
) : (
<h3>Loading...</h3>
)}
</ol>
</div>
);
}
}
export default App;
Upvotes: 1
Views: 7312
Reputation: 10975
To achieve expected result, use below option of storing the users list from API to a variable after using setState to update users
apiUsers = [];
fetchUsers() {
fetch(`https://jsonplaceholder.typicode.com/users`)
.then(response => response.json())
.then(data =>{
this.apiUsers = data;
this.setState({
users: data,
isLoading: false,
})
}
)
.catch(error => this.setState({ error, isLoading: false }));
}
Instead of filtering this.state.users
use the newly created variable - apiUsers
in onChangeHandler
onChangeHandler(e) {
console.log(e.target.value);
let newArray = this.apiUsers.filter((d)=>{
console.log(d)
let searchValue = d.name.toLowerCase();
return searchValue.indexOf(e.target.value) !== -1;
});
console.log(newArray)
this.setState({
users:newArray
})
}
working code for reference - https://stackblitz.com/edit/react-sncf1e?file=index.js
Issue: : state.users
array is getting updated without the copy of actual users list from api
Upvotes: 1
Reputation: 12806
Well, if you logically look at your current solution then you have the following situation
['John', 'Joe', 'Alfred']
J
and update the user state to ['John', 'Joe']
J
and update the user state to ['John', 'Joe']
cause the component completely forgot about Alfred
, you deleted hem in step 3So, you have several options, just filter the render before the mapping, depending on how many users, that shouldn't be that bad or create more than 1 property on the state, say filteredUsers
which contains the list of users
that match your filter, and work with that state instead.
The cool thing about your current filter, is that it will always go faster, as your resultset kind of trims down, so that's a good thing, but I doubt you'll be dealing with so many users that you need this.
Upvotes: 0
Reputation: 1569
It happens because you do not keep a track of the original values you get from the API. Once you start filtering it, you lose the full list.
I created a codesandbox: https://codesandbox.io/s/nostalgic-sunset-jig33 to fix your issue.
What I did is to add a initialUsers
value in the state
and use it to store the value coming from the API:
state = {
isLoading: true,
initialUsers: [],
users: [],
inputValue: "",
error: null
};
fetchUsers() {
fetch(`https://jsonplaceholder.typicode.com/users`)
.then(response => response.json())
.then(data =>
this.setState({
initialUsers: data,
isLoading: false
})
)
.catch(error => this.setState({ error, isLoading: false }));
}
and in the render
method, I switch between displaying the filtered list of the original list by checking if a text is entered in the input
element
(!!inputValue ? users : initialUsers).map(
user => {
const { username, name, id } = user;
return (
<li key={id}>
<p>
{name} <span>@{username}</span>
</p>
</li>
);
}
)
Upvotes: 0