React Native: отфильтрованный массив не смог передать значение в usestates

В моем проекте я получил список массивов из базы данных firestore в UseEffect(). После этого я отфильтровал список массивов в один массив с именем MedicineStock и передал каждое значение в новые состояния использования React (editMedicineName, editMedicineRegNo и editMedicineType) и сделал их значениями в моем вводе, однако он ничего не передает в каждый useStates, и мое поле ввода остается пустым.

function EditMedicine() {

  const { medicineId } = useParams();

  const [medicine, setMedicine] = useState([]);

  useEffect(() => {
    const q = query(collection(db, 'medicine'), where("isAccepted", "==", true) )
    onSnapshot(q, (querySnapshot) => {
      setMedicine(querySnapshot.docs.map(doc => ({
        id: doc.id,
        data: doc.data()
      })))
    })
  },[])

  const medicineStock = medicine.filter(medicineStock => medicineStock.id === medicineId);

  const [editMedicineName, setEditMedicineName] = useState(medicineStock.map(item => item.data.medicineName).toString());
  const [editMedicineRegNo, setEditMedicineRegNo] = useState(medicineStock.map(item => item.data.medicineRegNo).toString());
  const [editMedicineType, setEditMedicineType] = useState(medicineStock.map(item => item.data.medicineType).toString());


  return (
    <div className='editMedicine'>
      <div className="editMedicine-title">Edit Medicine</div>
      <form className="editMedicine-form" >
          <div>
            <p>
              <label className="editMedicine-dataTitle">Name:&nbsp;&nbsp;&nbsp;</label>
              <input 
                name="medicineName" 
                type="text" 
                required
                className="editMedicine-input" 
                value={editMedicineName}
                onChange={(e) => setEditMedicineName(e.target.value)}
              />
            </p>
            <p>
              <label className="editMedicine-dataTitle">Reg No:&nbsp;&nbsp;&nbsp;</label>
              <input 
                name="medicineRegNo" 
                type="text" 
                required
                className="editMedicine-input" 
                value={editMedicineRegNo}
                onChange={(e) => setEditMedicineRegNo(e.target.value)}
              />
            </p>
            <p>
              <label className="editMedicine-dataTitle">Type:&nbsp;&nbsp;&nbsp;</label>
              <select className="editMedicine-input" value={editMedicineType} onChange={(e) => setEditMedicineType(e.target.value)}>
                <option value="Tablet">Tablet</option>
                <option value="Capsule">Capsule</option>
                <option value="Liquid">Liquid</option>
                <option value="Spray">Spray</option>
                <option value="Cream">Cream</option>
                <option value="Ointment">Ointment</option>
              </select>
            </p>
          </div>
        </form>
    </div>
  )
}

Я устал в console.log для MedicineStock, который содержит отфильтрованный одиночный массив в объекте медицины, и консоль возвращает это:

[]
length: 0
[[Prototype]]: Array(0)

[{…}]
0:
data:
donationId: "XOCXD3FFaWGjO3oZcJmc"
medicineName: "Fluticasone Nasal Spray BP 50 mcg"
medicineRegNo: "MAL14125126AZ"
medicineType: "Spray"
[[Prototype]]: Object
id: "Ko6KqA9nyIotUVOkmfPL"
[[Prototype]]: Object
length: 1
[[Prototype]]: Array(0)

Где первый console.log не возвращает значения, а второй console.log возвращает значение, которое я хочу поместить в состояния использования React. Есть ли какое-нибудь решение для решения этой проблемы?

Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
Приложение для отслеживания бюджета на React js для начинающих
Приложение для отслеживания бюджета на React js для начинающих
Обучение на практике - это проверенная тема для достижения успеха в любой области. Если вы знаете контекст фразы "Практика делает человека...
Стоит ли использовать React в 2022 году?
Стоит ли использовать React в 2022 году?
В 2022 году мы все слышим о трендах фронтенда (React, Vue), но мы не знаем, почему мы должны использовать эти фреймворки, когда их использовать, а...
Как передать состояние или данные в react-router v6
Как передать состояние или данные в react-router v6
react-router - это лучшая библиотека для работы с маршрутизацией в reactjs. С помощью react-router вы передаете состояние или данные от одного...
0
0
29
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я выделю проблемы и решения, связанные с вашим кодом:

  • Когда вы пытаетесь инициализировать свои входы useState, массив medicine еще не готов, поскольку вызов firestore является асинхронным, поэтому, когда поступает массив medicine, начальные значения входов useState(editMedicinName...) будут привязаны к пустому массиву.
  • У вас также есть некоторые проблемы с манипулированием данными, поскольку вы помещаете type Array внутри своих editMedicine**** состояний, но затем в обработчиках ввода onChange вы передаете строки: setEditMedicineName(e.target.value)}
  • Решение первой проблемы — работа с хуком React useEffects. Первый useEffect выполняется только один раз при монтировании, он вызывает пожарная база данных, извлекает список лекарств и устанавливает его в состояние вашей реакции medicine. Когда medicine обновляется, срабатывает второй useEffect, и внутри него вы можете обновить свои входные базовые значения.
  • Вторая проблема решается путем установки непосредственно строковых значений в качестве входных базовых значений.
  • И последнее, но не менее важное: я ожидаю, что вы не захотите показывать свою форму, пока не получать medicine данные, поэтому рекомендуется поместить троичный оператор после возврата, чтобы вы могли показывать счетчик во время загрузки операции.
function EditMedicine() {
  const { medicineId } = useParams();

  const [medicine, setMedicine] = useState([]);

  useEffect(() => {
    const q = query(
      collection(db, 'medicine'),
      where('isAccepted', '==', true)
    );
    onSnapshot(q, (querySnapshot) => {
      // setMedicine is called asynchronously
      setMedicine(
        querySnapshot.docs.map((doc) => ({
          id: doc.id,
          data: doc.data(),
        }))
      );
    });
  }, []);
  // Filter medicine array, when it will be retrieved from firestore
  const medicineStock = useMemo(
    () =>
      medicine.filter((medicineStock) => medicineStock.id === medicineId)[0]
        .data,
    [medicine]
  );

  const [editMedicineName, setEditMedicineName] = useState(null);
  const [editMedicineRegNo, setEditMedicineRegNo] = useState(null);
  const [editMedicineType, setEditMedicineType] = useState(null);

  /* Set editMedicineName, editMedicineRegNo and editMedicineType when medicine array is retrieved */

  useEffect(() => {
    if (medicine) {
      setEditMedicineName(medicineStock.medicineName);
      setEditMedicineRegNo(medicineStock.medicineRegNo);
      setEditMedicineType(medicineStock.medicinType);
    }
  }, [medicine]);

  return !medicine ? "Loading..." :(
    <div className="editMedicine">
      <div className="editMedicine-title">Edit Medicine</div>
      <form className="editMedicine-form">
        <div>
          <p>
            <label className="editMedicine-dataTitle">
              Name:&nbsp;&nbsp;&nbsp;
            </label>
            <input
              name="medicineName"
              type="text"
              required
              className="editMedicine-input"
              value={editMedicineName}
              onChange={(e) => setEditMedicineName(e.target.value)}
            />
          </p>
          <p>
            <label className="editMedicine-dataTitle">
              Reg No:&nbsp;&nbsp;&nbsp;
            </label>
            <input
              name="medicineRegNo"
              type="text"
              required
              className="editMedicine-input"
              value={editMedicineRegNo}
              onChange={(e) => setEditMedicineRegNo(e.target.value)}
            />
          </p>
          <p>
            <label className="editMedicine-dataTitle">
              Type:&nbsp;&nbsp;&nbsp;
            </label>
            <select
              className="editMedicine-input"
              value={editMedicineType}
              onChange={(e) => setEditMedicineType(e.target.value)}
            >
              <option value="Tablet">Tablet</option>
              <option value="Capsule">Capsule</option>
              <option value="Liquid">Liquid</option>
              <option value="Spray">Spray</option>
              <option value="Cream">Cream</option>
              <option value="Ointment">Ointment</option>
            </select>
          </p>
        </div>
      </form>
    </div>
  );
}

Спасибо за подробное объяснение! Я постараюсь реализовать ваши решения и сообщить, решена ли проблема через несколько часов, так как здесь уже полночь.

Jacky cky 09.04.2022 17:43

Привет, вот обновление: я все еще не могу заставить его работать, второй useEffect, где установка editMedicine**** будет иметь проблемы с чтением medicineStock, поскольку терминал возвращает В React Hook useEffect отсутствуют зависимости: «medicineStock.medicineName», «medicineStock.medicineRegNo», «medicineStock.medici‌​neType». Либо включите их, либо удалите массив зависимостей. Можете ли вы помочь мне в этом вопросе?

Jacky cky 10.04.2022 07:18

Если вы жалуетесь, что вы не установили все зависимости. Это будет работать даже без них, так как medicine dep будет гидратировать MedicineStock in useMemo, но если вы не хотите подавлять lint warn, поместите эти значения в массив deps. Вы также можете заменить medicine на medicineStock во втором полезном эффекте.

Cesare Polonara 10.04.2022 07:35
Ответ принят как подходящий

Существует несколько неправильных представлений о том, как useState работает в вашем коде и вообще о том, как вы можете инициировать повторную визуализацию.

Что происходит в вашем коде:

  1. Вы инициализируете свойство состояния medicine пустым массивом -> OK
  2. Вы объявляете новую константу medicineStock, которая является отфильтрованной версией medicine. На данный момент medicineStock тоже пустой массив.
  3. Вы инициализируете 3 новых свойства состояний с начальным значением, основанным на MedicineStock. Это создаст 3 новых пустых строки. Здесь вы должны понимать, что initialValue состояния предназначен не для его обновления, а только для его инициализации. Если вы обновляете MedicineStock, например, он не будет обновляться editMedicineName.
  4. Затем вы получаете некоторые данные и вызываете setMedicine для их сохранения. Это приведет к запуску нового рендеринга. Таким образом, тело вашей функции будет прочитано снова. Конечно, в этот момент medicineStock будет иметь новое значение, поэтому вы можете увидеть это в своем журнале. Но, как я уже говорил, это не обновит editMedicineName, editMedicineRegNo и editMedicineType. Единственный способ обновить эти значения — вызвать их собственные сеттеры.

Как это исправить:

Я действительно не понимаю, чего вы пытаетесь достичь здесь, но простым исправлением для вашего кода будет:

  const [editMedicineName, setEditMedicineName] = useState();
  const [editMedicineRegNo, setEditMedicineRegNo] = useState();
  const [editMedicineType, setEditMedicineType] = useState();

  useEffect(() => {
    const q = query(collection(db, 'medicine'), where("isAccepted", "==", true) )
    onSnapshot(q, (querySnapshot) => {
      const medecine = querySnapshot.docs.map(doc => ({
        id: doc.id,
        data: doc.data()
      }));

      const medicineStock = medicine.filter(medicineStock => medicineStock.id === medicineId);

      setEditMedicineName(medicineStock.map(item => item.data.medicineName).toString());
      setEditMedicineRegNo(medicineStock.map(item => item.data.medicineRegNo).toString());
      setEditMedicineType(medicineStock.map(item => item.data.medicineType).toString());
    })
  },[])
    

Спасибо! Я решил свою ошибку на вашем примере, и данные о запасах лекарств успешно перешли в состояния использования React.

Jacky cky 10.04.2022 07:26

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