Как я могу получить исходные данные в дочернем компоненте, а затем извлечь объект из хранилища?

#javascript #reactjs #redux #react-redux

#javascript #reactjs #сокращение #реагировать-redux

Вопрос:

Мой родительский компонент <Room/> создает дочерние компоненты <RoomSensor/> , когда родительский компонент создает эти дочерние компоненты, я также отправляю в <RoomSensor/> uuid, по этому uuid я извлекаю данные датчика из серверной части.

Хранилище — это массив объектов.

 // Parent <Room/>

return props.sensors.map((uuid, index) => {
  return <RoomSensor key={index} uuid={uuid}/>
})
 
 // Children <RoomSensor/>

const RoomSensor = props => {
  useEffect(() => {
    console.log("useEffect")
    props.fetchSensor(props.uuid)
  }, [props.uuid])

  console.log(props.sensor)

  return (
    <div className={"col-auto"}>
      <small><b>{props.sensor.value}</b></small>
    </div>
  )

}


let mapStateToProps = (state, props) => {
  return {
    sensor: filterSensor(state, props.uuid)
  }
}

let mapDispatchToProps = {
  fetchSensor,
}

export default connect(mapStateToProps, mapDispatchToProps)(RoomSensor)
 
 // Selectors
export const getSensor = (state, uuid) => {
  return _.filter(state.sensors, ["uuid", uuid])[0]
}


export const filterSensor = createSelector(
  getSensor,
  (sensor) => sensor
)
 

И я не могу понять двух вещей:

  1. Когда я делаю обновление, я получаю.
 TypeError: Cannot read property 'uuid' of undefined
 

Я понимаю, что в состоянии еще нет данных, вот почему возникает такая ошибка. Можно ли не отображать компонент до тех пор, пока данные не поступят с сервера?

  1. Если я комментирую <small><b>{props.sensor.value}</b></small> , никаких ошибок не происходит, данные появляются в хранилище, затем я раскомментирую эту строку и вуаля, все работает. Но в консоли я вижу слишком много изменений компонентов. Что я делаю не так? Что-то не так с селектором? введите описание изображения здесь

В общем, я хочу, чтобы каждый компонент датчика отображался независимо от других.

Ответ №1:

Следующее основано на нескольких предположениях, полученных из общего кода и выходных данных:

  1. В настоящее время существует жестко запрограммированный список из 4 идентификаторов UUID датчиков.
  2. createSelector из reselect пакета.
  3. _ ссылается на импорт lodash пакета.

«Возможно ли не отображать компонент до тех пор, пока данные не поступят с сервера?»

Короткий ответ на этот вопрос — да. Для достижения этой цели существует несколько подходов, поэтому вам нужно оценить, что лучше всего соответствует структуре приложения. Вот 2 подхода для рассмотрения:

  1. Извлеките список датчиков с сервера. Инициализируйте хранилище пустым списком и заполните его после получения данных с сервера.
  2. В getSensor возвращает значение по умолчанию, если uuid отсутствует в списке.

В любом случае, я бы рекомендовал добавить состояние по умолчанию в хранилище. Это поможет уменьшить объем кода, необходимого для обработки граничных случаев.

Вот примерный пример того, как могут выглядеть новые редуктор и селектор для (1):

 export const storeReducer = (state, action) => {
  let nextState = state;
  if (!state) {
    // State is uninitialized, so give it a default value
    nextState = {
      sensors: [],
    };
  }

  switch (action.type) {
    case 'RECEIVE_SENSORS':
      // We received sensor data, so update the value in the store
      nextState = {
        ...nextState,
        sensors: action.sensors,
      };
      break;
    default:
      break;
  }

  return nextState;
};

export const getSensors(state) {
  return state.sensors;
}
 

Действие при получении данных с сервера может выглядеть примерно так:

 dispatch({
  sensors,
  type: 'RECEIVE_SENSORS',
})
 

«… в консоли я вижу слишком много перезаписанных компонентов [rs]»

Без дополнительного контекста трудно точно сказать, почему происходит повторная визуализация, но наиболее вероятной причиной является то, что каждый вызов props.fetchSensor(props.uuid) изменяет данные в хранилище (например, если он перезаписывает данные).

Из вывода консоли, которым вы поделились, мы видим, что существует 16 повторных рендерингов, что может произойти из-за:

  1. Каждый из 4 экземпляров RoomSensor вызовов fetchSensor
  2. Это приводит к 4 обновлениям состояния хранилища
  3. Каждое обновление состояния хранилища заставляет React оценивать каждый экземпляр RoomSensor для повторного рендеринга
  4. Следовательно, 4 обновления состояния x 4 оцененных компонента = 16 повторных рендерингов

React довольно эффективен, и если ваш компонент возвращает то же значение, что и при предыдущем запуске, он знает, что не нужно обновлять DOM. Таким образом, влияние на производительность, вероятно, на самом деле не так значительно.

Тем не менее, если приведенная выше теория верна и вы хотите уменьшить количество повторных рендерингов, одним из способов сделать это было бы проверить, совпадают ли данные, которые вы получаете с сервера, с теми, что уже есть в хранилище, и, если да, пропустить обновление хранилища.

Например, fetchSensor может быть обновлено чем-то вроде:

 const existingData = getSensor(getState(), uuid);
const newData = fetch(...);
// Only update the store if there's new data or there's a change
if (!existingData || !_.isEqual(newData, existingData)) {
  dispatch(...);
}
 

Для этого потребуется обновление getSensor , чтобы вернуть ложное значение (например null ), если uuid не найден в списке датчиков.


Один дополнительный совет

In Room RoomSensor отображается с его key помощью на основе индекса элемента в массиве. Поскольку uuid должно быть уникальным, вы можете использовать это как key вместо (т.Е. <RoomSensor key={uuid} uuid={uuid} /> ). Это позволило бы реагировать на базовые обновления RoomSensor только на uuid вместо того, чтобы также учитывать порядок списка.