Let's say I have a piece of logic (e.g. a search bar) and I want to display a spinner during this time. So I have the following code:
this.state = {
searching: false,
}
invokeSearch() {
this.setState({ searching: true })
const fuse = new Fuse(arrayOfData, { keys: [name: 'title'] })
const searchResults = fuse.search(searchTerm)
this.setState({ searching: false })
}
I'm storing the searching
boolean in state so I can pass it to the component to show or hide the spinner.
I understand that this won't work because setState
is asynchronous, thus according to the docs, I can use the callback instead.
The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead.
So my code now looks like this:
this.state = {
searching: false,
}
invokeSearch() {
this.setState({ searching: true }, () => {
const fuse = new Fuse(arrayOfData, { keys: [name: 'title'] })
const searchResults = fuse.search(searchTerm)
this.setState({ searching: false })
})
}
However, this doesn't work as expected. Rendering this.state.searching
both as a <Text>
component or in some ternary logic to show/hide the spinner does not visibly change from false.
I would guess it's something to do with the asynchronous, batching nature of setState which is causing this not to work?
In order to solve this I have been using the following solution which to me seems like a hack:
this.state = {
searching: false,
}
invokeSearch() {
this.setState({ searching: true }
setTimeout(() => {
const fuse = new Fuse(arrayOfData, { keys: [name: 'title'] })
const searchResults = fuse.search(searchTerm)
this.setState({ searching: false })
}, 0)
}
Can anyone explain to me why the callback does not work as I expect and how I might solve this in a more elegant manner?
EDIT: REVISED PROBLEM
Thank you for all your insightful answers & comments. After investigating further, I feel that Fuse is not a good example for this issue. I have another example with some asynchronous Redux which I will describe below
When I click on a button, my goal is to show a spinner, update the Redux store (which in turn re-renders a list of items), and then hide the spinner. The Redux is using Thunk so it's asynchronous and I have a callback setup on the action. The reducer isn't doing anything fancy, just updating the Redux store with the new data value.
// action
export function updateFilters(filters, successCallback = () => {}) {
return (dispatch) => {
dispatch({ type: ACTIONS.UPDATE_FILTERS, data: filters })
successCallback()
}
}
// component
changeFilter = (filters) => {
this.setState(() => ({
loading: true,
}), () => {
updateFilters(filters, () => {
this.setState({
loading: false,
})
})
}
}
The spinner still doesn't render!