Реагировать: получить данные из firebase и сохранить, используя условие гонки setState

Кажется, у меня есть какое-то состояние гонки с 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) })
      })
  }
}
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
0
229
2

Ответы 2

Не должно быть никаких условий гонки, поскольку 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, если он все еще не работает.

Надеюсь, это поможет!

Спасибо за помощь! Это было хорошо, но не решило мою проблему. Моя проблема возникла, когда я удалял элемент из DOM, а затем добавлял его обратно. Я решил проблему, скрыв элементы вместо того, чтобы удалять и добавлять их позже. Не знаю, почему, но проблема возникает только после удаления, а затем добавления элемента обратно в DOM. Эта проблема не возникла при самом первом добавлении элемента, чтобы он отключался при запуске. Почему?

etayluz 10.06.2018 08:13

Я думаю, что у меня есть представление о том, что может происходить, но мне нужно попробовать это с локальным проектом, если вы не можете создать StackBlitz, чтобы продемонстрировать проблему с помощью имитационного проекта Firebase. Просто чтобы полностью разобраться в проблеме, пара вопросов. Вы указываете, что добавляются / удаляются несколько экземпляров компонента ClientsTable в одном представлении? Если да, то выполняют ли они все тот же самый точный запрос Firebase, что и в вашем вопросе, или они отличаются использованием чего-то вроде orderByChild() или equalTo() Firebase?

Alexander Staroselsky 11.06.2018 16:17

Или когда вы говорите «скрытие элементов», вы имеете в виду отдельные элементы списка, возвращаемые запросом списка Firebase? Выполняет ли ClientsTable действия Firebase, такие как push(), set(), remove(), для элементов, возвращенных из запроса в componentWillMount(), в вашем вопросе? Возможно, вам потребуется обновить свой вопрос, добавив в него дополнительную информацию о функциональных возможностях ClientsTable.

Alexander Staroselsky 11.06.2018 16:26

Проблема в том, что вам нужно очистить ссылки на Firebase, иначе это приведет к утечке памяти. В React это обычно делается в componentWillUnmount.

Используя ваш пример, вам нужно добавить следующее:

componentWillUnmount: function() {
  this.clientsRef.off();
}

Вам также нужно будет изменить свой код в componentDidMount, чтобы вы использовали this.clientsRef вместо использования локальной переменной.

Вы можете прочитать об этом больше на Блог Firebase.

Другие вопросы по теме