Кажется, у меня есть какое-то состояние гонки с setState при чтении данных из Firebase. После загрузки компонента прослушиватель child_added вызывается столько, сколько клиентских записей находится в таблице clients, но только последняя клиентская запись фактически сохраняется в состоянии, использующем setState. Я знаю, что это связано с задержкой в setState, где он работает только после завершения цикла, поэтому возникает состояние гонки с несколькими вызовами setState. Как мне исправить это, чтобы все записи о клиентах правильно хранились в this.state.clients?
class ClientsTable extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
clients: [],
};
}
componentWillMount(){
let clientsRef = fire.database().ref('clients').orderByKey().limitToLast(100);
clientsRef.on('child_added', snapshot => {
let client = snapshot.val();
client.id = snapshot.key
this.setState({ clients: [client].concat(this.state.clients) })
})
}
}





Не должно быть никаких условий гонки, поскольку setState() ставит в очередь изменение при каждом вызове, в этом случае каждый моментальный снимок client, полученный в результате события child_added, включая существующий client и добавленный новый client.
Проблема действительно может быть связана с вашим синтаксисом для setState(), который может каждый раз изменять / возвращать this.state.clients. Вместо этого попробуйте использовать spread syntax... для добавления объектов client к this.state.clients и обновления / слияния this.state неизменным способом.
class ClientsTable extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
clients: []
};
}
componentWillMount(){
const clientsRef = fire.database().ref('clients').orderByKey().limitToLast(100);
clientsRef.on('child_added', snapshot => {
const client = { ...snapshot.val(), id: snapshot.key };
this.setState({
...this.state,
clients: [...this.state.clients, client]
});
});
}
}
Используя личный проект Firebase, я смог преобразовать каждый элемент в эквивалент this.state.clients и отобразить их соответственно как в componentDidMount, так и в componentWillMount. Если вы хотите, вы можете создать фиктивный проект Firebase с фиктивными данными / структурой, соответствующими вашему фактическому проекту, и сгенерировать StackBlitz, если он все еще не работает.
Надеюсь, это поможет!
Я думаю, что у меня есть представление о том, что может происходить, но мне нужно попробовать это с локальным проектом, если вы не можете создать StackBlitz, чтобы продемонстрировать проблему с помощью имитационного проекта Firebase. Просто чтобы полностью разобраться в проблеме, пара вопросов. Вы указываете, что добавляются / удаляются несколько экземпляров компонента ClientsTable в одном представлении? Если да, то выполняют ли они все тот же самый точный запрос Firebase, что и в вашем вопросе, или они отличаются использованием чего-то вроде orderByChild() или equalTo() Firebase?
Или когда вы говорите «скрытие элементов», вы имеете в виду отдельные элементы списка, возвращаемые запросом списка Firebase? Выполняет ли ClientsTable действия Firebase, такие как push(), set(), remove(), для элементов, возвращенных из запроса в componentWillMount(), в вашем вопросе? Возможно, вам потребуется обновить свой вопрос, добавив в него дополнительную информацию о функциональных возможностях ClientsTable.
Проблема в том, что вам нужно очистить ссылки на Firebase, иначе это приведет к утечке памяти. В React это обычно делается в componentWillUnmount.
Используя ваш пример, вам нужно добавить следующее:
componentWillUnmount: function() {
this.clientsRef.off();
}
Вам также нужно будет изменить свой код в componentDidMount, чтобы вы использовали this.clientsRef вместо использования локальной переменной.
Вы можете прочитать об этом больше на Блог Firebase.
Спасибо за помощь! Это было хорошо, но не решило мою проблему. Моя проблема возникла, когда я удалял элемент из DOM, а затем добавлял его обратно. Я решил проблему, скрыв элементы вместо того, чтобы удалять и добавлять их позже. Не знаю, почему, но проблема возникает только после удаления, а затем добавления элемента обратно в DOM. Эта проблема не возникла при самом первом добавлении элемента, чтобы он отключался при запуске. Почему?