UPDATED:
A couple of things:
1) Your useEffect is subscribing to lists which is being set within this useEffect, so it's triggering a re-run.
2) You're attempting to update the item and then re-fetch all of the data including the updated item. You don't need to do this. Once you have your "initial state" from Firebase, all of the modifications can happen to that list within state. Yes, you'll call to do the add/update/delete, but you don't need to call to get a brand new list each time because you know what information you've changed and can simply update your state list to reflect this while also calling to actually update the underlying data so that when you navigate away your list from Firebase reflects the list that was in state.
SO!
Remove the lists from the [] at the end so your code so it only runs on mount and looks like the following:
const [lists, setLists] = useState([]);
useEffect(() => {
const dataArray = [];
/** handleWidgets */
listsRef
.once('value', snap => {
snap.forEach(function(result) {
firebase
.database()
.ref('lists')
.child(result.key)
.once('value', snap => {
if (snap.val()) dataArray.push(snap.val());
});
});
})
.then(function() {
setLists(dataArray);
});
}, []); // removed lists from subscription []
Then, let's say in an add method, it would look something like the following:
addItem = listItem => {
// make copy of array in state
const newLists = [...lists];
// add item to list
newLists.push(listItem);
// update list in DB with firebase
/* firebase call here with new list item */
// update state lists with new array that has new item
setLists(newLists)
}
And so on and so forth from the update/delete methods. You're going to just use that state value without ever calling Firebase for an updated list because you already have it.