Populacja tablicy danymi wczytanymi z HTTP

0

Dzień dobry,
mam taki problem

po kliknięciu w przycisk (<button>) zostaje wykonana funkcja, która wczytuje dane do tablicy o nazwie zlecenie_zawartosc

Przykład: WczytajDaneDotablicy(53);

const [zlecenie_zawartosc, setzlecenie_zawartosc] = React.useState([]);
const WczytajDaneDotablicy = (id) => {
  fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?id=" + id)
    .then(res => res.json())
    .then(json => setzlecenie_zawartosc(json));
}

przykład użycia:

const Przycisk_Wybierz = () => {
  WczytajDaneDotablicy(wybrane_zlecenie);
  console.log("Ilosc:", zlecenie_zawartosc[0].ilosc);
}

format pobranych danych z bazy danych:

[{"id":58,"kontrahent":"JanKowalski","telefon":123123129,"data_zbioru":"2022-10-31T23:00:00.000Z","ilosc":5,"typ_pisklecia":2,"uwagi":""}]

Niestety dane w tablicy nie są aktualizowane na bieżąco. co powoduje np: błąd podczas odczytu zawartości.

Uncaught TypeError: Cannot read properties of undefined (reading 'ilosc').

Domyślam się że należy tutaj wykorzystać useEffect() tylko jak zbudować poprawnie algorytm?

Oto treść błędu:

Oto pełna treść błędu:
komponent_dokument_modyfikuj.js:149 Uncaught TypeError: Cannot read properties of undefined (reading 'ilosc')
at KomponentModyfikujDokument (komponent_dokument_modyfikuj.js:149:1)
at renderWithHooks (react-dom.development.js:16305:1)
at updateFunctionComponent (react-dom.development.js:19588:1)
at beginWork (react-dom.development.js:21601:1)
at beginWork$1 (react-dom.development.js:27426:1)
at performUnitOfWork (react-dom.development.js:26557:1)
at workLoopSync (react-dom.development.js:26466:1)
at renderRootSync (react-dom.development.js:26434:1)
at recoverFromConcurrentError (react-dom.development.js:25850:1)
at performSyncWorkOnRoot (react-dom.development.js:26096:1)
KomponentModyfikujDokument @ komponent_dokument_modyfikuj.js:149
renderWithHooks @ react-dom.development.js:16305
updateFunctionComponent @ react-dom.development.js:19588
beginWork @ react-dom.development.js:21601
beginWork$1 @ react-dom.development.js:27426
performUnitOfWork @ react-dom.development.js:26557
workLoopSync @ react-dom.development.js:26466
renderRootSync @ react-dom.development.js:26434
recoverFromConcurrentError @ react-dom.development.js:25850
performSyncWorkOnRoot @ react-dom.development.js:26096
flushSyncCallbacks @ react-dom.development.js:12042
(anonymous) @ react-dom.development.js:25651

a występuje przy wykonaniu poniższej linijki:

  {wyswietl_panel && <KomponentModyfikujDokumentPanel ilosc={zlecenie_zawartosc[0].ilosc} wyswietl={wyswietl_panel} anuluj={UkryjPanel} dane={zlecenie_zawartosc} typ_pisklecia={zlecenie_zawartosc[0].typ_pisklecia}  data_zbioru={zlecenie_zawartosc[0].data_zbioru} uwagi={zlecenie_zawartosc[0].uwagi}/>}

Pozdrawiam,
Łukasz

0
virusek391 napisał(a):

Niestety dane w tablicy nie są aktualizowane na bieżąco. co powoduje np: błąd podczas odczytu zawartości.

Uncaught TypeError: Cannot read properties of undefined (reading 'ilosc').

Żeby Ci pomóc samemu to znaleźc, polecam dodaj console.log(zlecenie_zawartosc);, i zobacz co się stanie

const Przycisk_Wybierz = () => {
  WczytajDaneDotablicy(wybrane_zlecenie);
  console.log("Ilosc:", zlecenie_zawartosc); // dodaj to i zobacz jaki masz efekt
  console.log("Ilosc:", zlecenie_zawartosc[0].ilosc);
}
virusek391 napisał(a):
const WczytajDaneDotablicy = (id) => {
  fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?id=" + id)
    .then(res => res.json())
    .then(json => setzlecenie_zawartosc(json));
}

Nie buduj URL w taki sposób, wartości w URL muszą być zaenkodowane w odpowiedni sposób, np jeśli masz spację lub znak = się w id to one muszą być reprezentowane jako %20 lub +. Zamiast tego zrób

fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?" + new URLSearchParams({id}));
0

Dostajesz błąd, ponieważ jest to funkcja asynchroniczna i wszystko zostaje wywołane w innej kolejności

const Przycisk_Wybierz = () => {
  console.log("Ilosc:",zlecenie_zawartosc[0].ilosc);   // 1) uruchomienie console.log'a
  WczytajDaneDotablicy(wybrane_zlecenie);              // 2) pobranie danych                                                    
}                                                      // 3) i na samym końcu aktualizacja stanu komponentu

więc żeby wyświetlić zaktualizowany stan to musiałbyś skorzystać z np useEffect

https://reactjs.org/docs/hooks-reference.html#useeffect

const [data, setData] = useState([]);

useEffect(() => {
  if (!data.length) {
    return; // wyjście z funkcji jeśli nie ma żadnych danych w tablicy
  }

  console.dir(data);
}, [data.length]);

const changeData = (id) => {
   fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?id=" + id)
    .then(res => res.json())
    .then(json => setData(json));
}
0
Xarviel napisał(a):

Dostajesz błąd, ponieważ jest to funkcja asynchroniczna i wszystko zostaje wywołane w innej kolejności

const Przycisk_Wybierz = () => {
  console.log("Ilosc:",zlecenie_zawartosc[0].ilosc);   // 1) uruchomienie console.log'a
  WczytajDaneDotablicy(wybrane_zlecenie);              // 2) pobranie danych                                                    
}                                                      // 3) i na samym końcu aktualizacja stanu komponentu

więc żeby wyświetlić zaktualizowany stan to musiałbyś skorzystać z np useEffect

https://reactjs.org/docs/hooks-reference.html#useeffect

const [data, setData] = useState([]);

useEffect(() => {
  if (!data.length) {
    return; // wyjście z funkcji jeśli nie ma żadnych danych w tablicy
  }

  console.dir(data);
}, [data.length]);

const changeData = (id) => {
   fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?id=" + id)
    .then(res => res.json())
    .then(json => setData(json));
}

W moim przypadku zrobiłem tak:

useEffect(() => {
    if (!zlecenie_zawartosc.length) {
      console.log("pusto");
      return; // wyjście z funkcji jeśli nie ma żadnych danych w tablicy
    }
    console.dir(zlecenie_zawartosc);
  }, [zlecenie_zawartosc.length]);

const WczytajDaneDotablicy = (id) => {
  fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?id=" + id)
    .then(res => res.json())
    .then(json => setzlecenie_zawartosc(json));
}

const Przycisk_Wybierz = () => {
  WczytajDaneDotablicy(wybrane_zlecenie);
  console.log("Ilosc:",zlecenie_zawartosc);  
}

Niestety nadal to samo :(

0

W funkcji Przycisk_Wybierz nie potrzebujesz żadnego console.loga.

Wcześniej wkleiłem Ci link do dokumentacji z opisem jak działa hook useEffect i trochę musisz poczytać na jego temat.

0

@virusek391: Nie możesz po prostu zrobić tak?

const WczytajDaneDotablicy = (id) => {
  fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?id=" + id)
    .then(res => res.json())
    .then(json => {
      console.log(json); // tutaj zobacz co jest?
      setzlecenie_zawartosc(json);
    });
}

const Przycisk_Wybierz = () => {
  WczytajDaneDotablicy(wybrane_zlecenie);
  console.log("Ilosc:",zlecenie_zawartosc);  
}

A jak nie, to po prostu wyrenderuj treść tego co masz w komponencie:

function Component({}) {
  const [zlecenie_zawartosc, setzlecenie_zawartosc] = React.useState([]);

  const WczytajDaneDotablicy = (id) => {
    fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?id=" + id)
      .then(res => res.json())
      .then(json => setzlecenie_zawartosc(json));
  }

  const Przycisk_Wybierz = () => {
    WczytajDaneDotablicy(wybrane_zlecenie);
    console.log("Ilosc:",zlecenie_zawartosc);  
  }

  return <div>
    {JSON.stringify(zlecenie_zawartosc)}
  </div>;
}

Bo cały czas robisz, nie wiem w sumie co, wygląda jakbyś miał jakiś problem - coś Ci nie działa, i wymyśliłeś sobie ze musisz gdzieś dodać console.log() żeby zobaczyć co się dzieje - spoko pomysł, tylko że z tym console.log() też sobie nie poradziłeś, i wpadłeś w kolejny problem. Klasyczny XY.

Pokaż najlepiej cały Twój komponent, i opisz - od początku - co Ci nie działa.

0
Riddle napisał(a):

@virusek391: Nie możesz po prostu zrobić tak?

const WczytajDaneDotablicy = (id) => {
  fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?id=" + id)
    .then(res => res.json())
    .then(json => {
      console.log(json); // tutaj zobacz co jest? 
      setzlecenie_zawartosc(json);
    });
}

const Przycisk_Wybierz = () => {
  WczytajDaneDotablicy(wybrane_zlecenie);
  console.log("Ilosc:",zlecenie_zawartosc);  
}

A jak nie, to po prostu wyrenderuj treść tego co masz w komponencie:

function Component({}) {
  const [zlecenie_zawartosc, setzlecenie_zawartosc] = React.useState([]);

  const WczytajDaneDotablicy = (id) => {
    fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?id=" + id)
      .then(res => res.json())
      .then(json => setzlecenie_zawartosc(json));
  }

  const Przycisk_Wybierz = () => {
    WczytajDaneDotablicy(wybrane_zlecenie);
    console.log("Ilosc:",zlecenie_zawartosc);  
  }

  return <div>
    {JSON.stringify(zlecenie_zawartosc)}
  </div>;
}

Bo cały czas robisz, nie wiem w sumie co, wygląda jakbyś miał jakiś problem - coś Ci nie działa, i wymyśliłeś sobie ze musisz gdzieś dodać console.log() żeby zobaczyć co się dzieje - spoko pomysł, tylko że z tym console.log() też sobie nie poradziłeś, i wpadłeś w kolejny problem. Klasyczny XY.

Pokaż najlepiej cały Twój komponent, i opisz - od początku - co Ci nie działa.

Prześledziłem ten kawałek kodu:

const WczytajDaneDotablicy = (id) => {
   fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?id=" + id)
     .then(res => res.json())
     .then(json => {
       console.log(json); // tutaj zobacz co jest? 
       setzlecenie_zawartosc(json);
     });
 }

i wychodzi na to że nie aktualizuje danych podczas wykonania polecenia: setzlecenie_zawartosc(json);,
natomiast podczas wykonania polecenia console.log(json); wszystkie dane zostały wyświetlone prawidłowo.

1
virusek391 napisał(a):
const WczytajDaneDotablicy = (id) => {
   fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?id=" + id)
     .then(res => res.json())
     .then(json => {
       console.log(json); // tutaj zobacz co jest? 
       setzlecenie_zawartosc(json);
     });
 }

i wychodzi na to że nie aktualizuje danych podczas wykonania polecenia: setzlecenie_zawartosc(json);,
natomiast podczas wykonania polecenia console.log(json); wszystkie dane zostały wyświetlone prawidłowo.

Myślę, że Twoim największym problemem na razie jest zrozumienie tego, w jaki sposób React renderuje komponenty, i jaki jest przepływ danych w dół komponentu.

Na ten moment polecam lekturę: https://reactjs.org/docs/hooks-intro.html przeczytaj to całe, zwłaszcza punkty 1., 3. oraz 4. Ci się bardzo przydadzą. Bez tego praca w Reacie będzie dla Ciebie niemal niemożliwa.

Musisz pamiętać, że kiedy pracujesz z komponentami, stanem, useState() i useEffect(), to one nie są "synchroniczne", to znaczy nie wykonują się jeden po drugim. Bierz pod uwagę, że komponent będzie renderowany w różnych momentach, z różnymi danymi, i Twój stan i effekt mogą też mieć różne wartości.

Twoim błędem jest założenie, żę w momencie w którym ustawisz stan z useState() w danym cyklu, to jego zmiana będzie od razu widoczna jako jego wartość w tym samym cyklu. Tak jednak nie jest:

const [value, setValue] = useState(2);

console.log(value);   // (int) 2
setValue(3); 
console.log(value);   // Twoje założenie jest, że tutaj będzie 3 - sprawdź, i zobacz co tu będzie

Tak na prawdę wartość 3 będzie w value dopiero w następnym renderze. Ogólnie - nie powinieneś próbować odczytać wartości state'u w miejscu w którym go ustawiasz - to jest niemal gwarantowane, że się nie uda.

1
virusek391 napisał(a):

Ok. A według ciebie co powinienem zrobić aby po kliknięciu buttonu została wczytana zawartość polecenia fetch do tablicy, a następnie tą tablice przesłać do innego komponentu ?

A przeczytałeś dokumentację którą Ci podlinkowałem?

Powinieneś zrobić to:

function Parent({}) {
  const [value, setValue] = useState(null);

  function fetchData() {
    fetch("from.php")
      .then(response => response.json())
      .then(fetchedValue => {
        setValue(fetchedValue)
      });
  }

  return <div>
    <button onClick={fetchData}>Fetch</button>
    <Child value={value}/>
  </div>
}

Jeśli chcesz gdziekolwiek wsadzić console.log(fetchedValue), to TYLKO w linijce ósmej, za setValue(fetchedValue), to jest jedyne miejsce gdzie możesz zobaczyć bezpośrednio co przyszło z AJAX'a.

0
Riddle napisał(a):
virusek391 napisał(a):

Ok. A według ciebie co powinienem zrobić aby po kliknięciu buttonu została wczytana zawartość polecenia fetch do tablicy, a następnie tą tablice przesłać do innego komponentu ?

A przeczytałeś dokumentację którą Ci podlinkowałem?

Powinieneś zrobić to:

function Parent({}) {
  const [value, setValue] = useState(null);

  function fetchData() {
    fetch("from.php")
      .then(response => response.json())
      .then(fetchedValue => {
        setValue(fetchedValue)
      });
  }

  return <div>
    <button onClick={fetchData}>Fetch</button>
    <Child value={value}/>
  </div>
}

Jeśli chcesz gdziekolwiek wsadzić console.log(fetchedValue), to TYLKO w linijce ósmej, za setValue(fetchedValue), to jest jedyne miejsce gdzie możesz zobaczyć bezpośrednio co przyszło z AJAX'a.

Witaj,
powiem szczerze że nie czytałem :)
Dzięki za przykład. Lubię uczyć się na konkretnych przykładach.
Trochę pokombinowałem i udało się. Poniżej zamieszczam najważniejsze zmiany w moim kodzie:

const [pobierz_zlecenia, setpobierz_zlecenia] = React.useState([]);
const [wybrane_zlecenie, setwybrane_zlecenie] = React.useState(0);
 useEffect(
    () => {
      if (wybrane_zlecenie !== 0) {
        WczytajDaneDotablicy(wybrane_zlecenie);
        setwybrane_zlecenie(0)
      }
    }, [wybrane_zlecenie]

function WczytajDaneDotablicy(id) {
  fetch("http://192.168.1.4:3333/pobierz_zlecenia_szczegolowe_wg_id/?id=" + id)
    .then(res => res.json())
    .then(json => setzlecenie_zawartosc(json));
  enqueueSnackbar('Zaznazono zlecenie numer: ' + id, { variant: "success", autoHideDuration: 4000 });
}
 const Przycisk_Wybierz = () => {
  if (wybrane_zlecenie.length === 0) {

    enqueueSnackbar('Proszę wybrać pozycje!', { variant: "warning", autoHideDuration: 6000 });
  }
  setwyswietl_panel(true);
  props.ukryj();
}
    return (
    <>
 {wyswietl_panel && <KomponentModyfikujDokumentPanel ilosc={zlecenie_zawartosc[0].ilosc} wyswietl={wyswietl_panel} anuluj={UkryjPanel} dane={zlecenie_zawartosc} typ_pisklecia={zlecenie_zawartosc[0].typ_pisklecia} data_zbioru={zlecenie_zawartosc[0].data_zbioru} uwagi={zlecenie_zawartosc[0].uwagi} />}
 <DataGrid
  rows={wiersz}
  columns={kolumny}
  pageSize={10}
  onSelectionModelChange={(wybrana_pozycja) => {
    if (wybrana_pozycja.length > 0) {
      setwybrane_zlecenie(wybrana_pozycja);
    }
  }}
  localeText={tabela_tlumaczenie_napisow}
/>
0
useEffect(
    () => {
      if (wybrane_zlecenie !== 0) {
        WczytajDaneDotablicy(wybrane_zlecenie);
        setwybrane_zlecenie(0)
      }
    }, [wybrane_zlecenie]
// ...

 onSelectionModelChange={(wybrana_pozycja) => {
    if (wybrana_pozycja.length > 0) {
      setwybrane_zlecenie(wybrana_pozycja);
    }
  }}

Wiesz, że robisz rzeczy naokoło, chyba tylko po to, żeby użyć useEffect? Chcesz wczytać dane do tablicy, to czemu nie striggerujesz czytania danych w handlerze onSelectionModelChange?
ogólnie mam wrażenie, że w swoim kodzie mieszają ci się 3 równoległe podejścia:

  1. imperatywność (rób to i to komputerze!)
  2. deklaratywność (drogi komputerze, sytuacja wygląda tak i tak)
  3. asynchroniczność (pewne rzeczy zajmują czas i trzeba na nie poczekać)

I wszystkie 3 podejścia są potrzebne. Jednak mieszają ci się, bo tak jakbyś nie bardzo wiedział, kiedy którego podejścia użyć i używasz ich randomowo. I tym sposobem odpalając imperatywny handler onSelectionModelChange, zmieniasz stan setwybrane_zlecenie, żeby spowodować deklatywne odpalenie efektu, który już będzie imperatywny, bo wybrane_zlecenie to tylko deklaratywna flaga do odpalania imperatywnego efektu, później ładujesz dane do tablicy imperatywnie, ale zapominasz o asynchroniczności...

Tak jakbyś nie miał dobrego modelu mentalnego.

Myślę, że na tym etapie potrzeba ci cofnąć się do początku, jeśli chodzi o rozumienie Reacta i np. też poczytanie jakichś przykładowych kodów, bo mam wrażenie, że piszesz niby w React, a w rzeczywistości w swoim własnym wymyślonym paradygmacie.

No i styl. Twoje nazewnictwo jest 1. nietypowe 2. niespójne.
co do 1. to zobacz sobie, jak zwykle się pisze (tj. camelCase). Po angielsku, a nie po polsku.
co do 2. to żeby to jedną, ale stosujesz ileś różnych konwencji:

  1. WczytajDaneDotablicy - PascalCase
  2. Przycisk_Wybierz - PascalCase + underscores
  3. setwybrane_zlecenie - underscores, ale nie do końca, bo czemu między set a wybrane nie ma underscore'a?

ogólnie jeden wielki chaos.

w JS i React się pisze zwykle tak:

const [fooBar, setFooBar] = React.useState(0);

1 użytkowników online, w tym zalogowanych: 0, gości: 1