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

Есть форма, в нее передаю данные с другой страницы, принимаю и перезаписываю в переменные useState для дальнейшего изменения. Выпадающий список для подобъекта формируется через запрос с выборкой по полю subobject. Выпадающий список для системы формируется, если выбран подобъект, иначе пустой список, при перевыборе подобъекта на другой, значение системы очищается.

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

При выборе значений в выпадающих поля функционал работает почти исправно - если в подобъекте выбрано поле, которое пришло до этого не формируется список для системы, если выбрано другое - то список формируется. При ручном выборе значения выводятся в текстовое поле, но значение слетает с текстового поля после изменения других полей на форме, например, содержания замечания - однако в переменных в логах выводит выбранное значение до этого.

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

экранная форма

import React, { useState, useEffect } from 'react';
import { Text, View, TextInput, ScrollView} from 'react-native';
import {useLocalSearchParams } from 'expo-router';
import ListOfSystem from '@/components/ListOfSystem';
import { Structure } from '../(tabs)/structure';
import ListOfSubobj from '@/components/ListOfSubobj';

export type ListToDrop = {
  label: string;
  value: string; 
};

export default function CreateNote() {
  const [array, setArray] = useState<Structure[]>([]);//данные получаю при вызове запроса
  const listSubObj = [];//список подобъектов из структуры
  const [noteListSubobj, setNoteListSubobj] = useState<boolean>(true);//ограничение на получение листа подобъектов только единожды 
  const listSystem = [];//список систем из структуры на соответствующий выбранный подобъект
  const [noteListSystem, setNoteListSystem] = useState<boolean>(false);//ограничение на отправку листа систем в компонент
  const [statusReq, setStatusReq] = useState(false);//для выпадающих списков, передача данных, когда True
  const [subObject, setSubObject] = useState('');
  const [systemName, setSystemName] = useState(' ');
  const [description, setDescription] = useState('');
  const [execut, setExecut] = useState('');
  const [bufsubobj, setBufsubobj] = useState('');
  const [bufsystem, setBufsystem] = useState('');

  const {subObject} = useLocalSearchParams();//получение подобъекта
  //далее получаю все данные по форме и с помощью useState записываю в переменные

  const TwoFunction = () => {
      if(systemName != bufsystem){
        setBufsystem(systemName);
      if (systemName != ' ' ){
        const filtered = array.filter(item => item.subObjectName === subObject);
        const filteredS = filtered[0].data.filter(item => item.systemName === systemName);
        if(filteredS.length != 0){
          setExecut(filteredS[0].ciwexecutor);
        }
        else{
          setExecut('');
          setSystemName(' ');
        }
        setNoteListSystem(false);
      }
      }  
  };


  useEffect(() => {
    //формирование выпадающего списка для подобъекта
    if(statusReq && noteListSubobj){//вызов происходит только один раз
      setNoteListSubobj(false);
      const buf = array.map(item => ({label: item.subObjectName, value: item.subObjectName}));
      listSubObj.push(...buf);
    }
    //формирование выпадающего списка для системы после того как выбран подобъект
    if (subObject ){
      const filtered = array.filter(item => item.subObjectName === subObject);
      console.log(filtered[0].data);
      for (const pnrsystemId in filtered[0].data) {
          const buf = filtered.map(item => ({label: item.data[pnrsystemId].systemName, value:  item.data[pnrsystemId].systemName}));
          console.log('listSystem',buf);
          listSystem.push(...buf);
      }  
      if(subObject != bufsubobj){ //это работает, но после каждого обновления subObject в systemName попадает с кеша(?) последнее значение
        setSystemName('');
        setExecut('');
        setBufsubobj(subObject);
      }
    }
    if (execut){
      submitData();
    } 
        
  }, [statusReq, noteListSubobj, subObject, systemName, execut]);

  const submitData = async () => {
        //запрос на запись данных в бд
}


  return (
        <View >

            <View >
              <Text >Подобъект</Text>
              <ListOfSubobj post = {subObject} list={listSubObj} statusreq={statusReq} onChange = {(subObj) => setSubObject(subObj)}/>
            </View>

          <Text >Система</Text>
          <ListOfSystem post = {systemName} subobj={subObject} list={listSystem} statusreq={noteListSystem} onChange = {(subObj) => setSystemName(subObj)}/>

          <Text >Содержание замечания</Text>
          <TextInput
            placeholderTextColor="#111"
            onChangeText={setDescription}
            value={description}
          />
        <CustomButton title="Добавить замечание" handlePress={TwoFunction} />
        </View>   
  );
}

ListOfSubobj


import React, { useEffect, useState } from 'react';
import { StyleSheet, Text, useWindowDimensions, View } from 'react-native';
import { Dropdown } from 'react-native-element-dropdown';

export type ListToDrop = {
    label: string;
    value: string; 
  };

type Props = {
    list: ListToDrop[];//список
    post: string;//значение из бд статуса для просмотра, при записи передавать пустую строку
    statusreq: boolean;//для обновления значения даты при получении даты с запроса
    onChange: (subobj: string, ) => void; // Функция для обновления значения
};

const ListOfSubobj = ({list, post, statusreq, onChange }: Props) => {
    const [value, setValue] = useState<string >();
    const [data, setData] = useState<ListToDrop[]>([]);
    const [isFocus, setIsFocus] = useState(false);
    const [startD, setStartD] = useState<boolean>(true);//при первом рендеринге поставить значения из бд 

    const fontScale = useWindowDimensions().fontScale;

    const ts = (fontSize: number) => {
        return (fontSize / fontScale)};

    if (statusreq && startD){//запись при первом и единственном рендеринге
        setValue(post);//значение из БД
        setStartD(false);
        setData(list);
    }
    
    if (value){
        onChange(value);
    }
 
    return (
       
        <View >
            <Dropdown
                data={data}
                search
                maxHeight={300}
                itemTextStyle={{fontSize: ts(14)}}
                labelField="label"
                valueField="value"
                placeholder={!isFocus ? 'Не выбрано' : 'Не выбрано'}
                searchPlaceholder="Search..."
                value={value}
                onFocus={() => setIsFocus(true)}
                onBlur={() => setIsFocus(false)}
                onChange={item => {
                    setValue(item.value);
                    setIsFocus(false);
                }}
            />
        </View>  
    );
};

export default ListOfSubobj;


ListOfSystem


import React, { useEffect, useState } from 'react';
import { StyleSheet, Text, useWindowDimensions, View } from 'react-native';
import { Dropdown } from 'react-native-element-dropdown';

export type ListToDrop = {
    label: string;
    value: string; 
  };

type Props = {
    list: ListToDrop[];//список
    post: string;//значение из бд статуса для просмотра, при записи передавать пустую строку
    onChange: (subobj: string, ) => void; // Функция для обновления значения 
};

const ListOfSystem = ({list, post, onChange}: Props) => {
    const [value, setValue] = useState<string >();
    const [data, setData] = useState<ListToDrop[]>([]);
    const [isFocus, setIsFocus] = useState(false);

    useEffect(
        () => {
        if(list){
            setValue(post);
            setData(list);
        }
        
        if(post != ' '){
            setValue(post);
            //setData(list);
            console.log('post List', list );
        }
        }, [ list]
    )
    
    if (value){
        onChange(value);
    }
 
    return (
       
        <View >
            <Dropdown
                data={data}
                search
                maxHeight={300}
                labelField="label"
                valueField="value"
                placeholder={!isFocus ? 'Не выбрано' : 'Не выбрано'}
                searchPlaceholder="Search..."
                value={value}
                onFocus={() => setIsFocus(true)}
                onBlur={() => setIsFocus(false)}
                onChange={item => {
                    setValue(item.value);
                    setIsFocus(false);
                }}
            />
        </View>
        
    );
};

export default ListOfSystem;



Ответы (1 шт):

Автор решения: Богдан Новотарский

У тебя несколько проблем в коде, которые могут вызывать этот баг. Давай разбираться.

  1. Состояние value в Dropdown не обновляется правильно В ListOfSubobj и ListOfSystem у тебя используется useState(value), но когда post обновляется (новое значение приходит с другой страницы), value остается старым. Как исправить: Добавь useEffect, который будет следить за post и обновлять value:
useEffect(() => {
    setValue(post);
}, [post]);
  1. Перезапись value внутри if (value)
if (value){
    onChange(value);
}

Этот код в ListOfSubobj и ListOfSystem вызовет onChange() при каждом ререндере, даже если пользователь ничего не выбирал. Это может приводить к зацикливанию. Исправь на:

useEffect(() => {
    if (value) {
        onChange(value);
    }
}, [value]);
  1. Передача list как пустого массива
listSystem.push(...buf);

У тебя listSystem — это просто пустой массив, объявленный в CreateNote. Но ListOfSystem использует этот массив как пропс list={listSystem}. Это значит, что в Dropdown список систем может не обновляться. Как исправить: Вместо listSystem используй useState:

const [listSystem, setListSystem] = useState<ListToDrop[]>([]);

Затем обновляй listSystem внутри useEffect:

useEffect(() => {
    if (subObject) {
        const filtered = array.filter(item => item.subObjectName === subObject);
        const newList = filtered.map(item => ({
            label: item.systemName,
            value: item.systemName
        }));
        setListSystem(newList);
    }
}, [subObject]);
  1. Очистка systemName при изменении subObject У тебя есть этот код:
if(subObject != bufsubobj){
    setSystemName('');
    setExecut('');
    setBufsubobj(subObject);
}

Однако systemName очищается, но в ListOfSystem useEffect не всегда это видит. Как исправить: Вместо '' лучше установить undefined, так как Dropdown может некорректно работать с пустыми строками:

setSystemName(undefined);
  1. Передача post в Dropdown В ListOfSystem и ListOfSubobj у тебя post не передается корректно, так как value не синхронизируется. Исправь:
<ListOfSystem 
    post={systemName} 
    subobj={subObject} 
    list={listSystem} 
    statusreq={noteListSystem} 
    onChange={(value) => setSystemName(value)}
/>

Теперь systemName всегда будет совпадать с post.

Попробуй эти изменения, и выпадающие списки должны начать правильно отображать переданное значение!

→ Ссылка