javascript - Read-once-muuttujan käyttäminen, joka on ladattu asynkronisesti react-hookin kautta


Leikin parhaillani Reactin hookien kanssa, mutta tällä hetkellä olen jumissa eri käyttötapausten yhdistämisessä.

Seuraava skenaario on se, mitä yritän saada toimimaan. Pitäisi olla yksi hook, jota kutsutaan useNationsAsync, ja joka hakee luettelon saatavilla olevista maista palvelimelta. Hookin sisällä tarkistan, onko luettelo jo ladattu/tallennettu localStorageen, jotta se ladataan vain kerran.

Etäkutsua varten käytän axios'n get-kutsua yhdessä await-avainsanan kanssa. Joten tämän "hookin" täytyy olla async. Olen toteuttanut sen seuraavasti:

export async function getNationsAsync({ }: IUseNationsProps = {}): Promise<NationResource[]> {
    const [storedNations, setStoredNations] = useLocalStorage<NationResource[]>("nations", null);

    if (storedNations == null) {
        const nationsResponse = await Axios.get<NationsGetResponse>("/v1/nations/");
        setStoredNations(nationsResponse.data.nations);
    }

    return storedNations;
}

useLocalStorage-hook on se, joka löytyy täältä (tyyppien kanssa tarkoitettu käytettäväksi vain TypeScript-kielen kanssa).

Viimeisessä FunctionalComponent-komponentissani haluan lukea maat vain kerran, joten ajattelin, että useEffect-hookin käyttäminen tyhjällä taulukolla olisi oikea paikka (koska tämä on pääosin sama kuin componentDidMount). Kuitenkin ajon aikana saan seuraavan virheen ensimmäisellä rivillä getNationsAsync-hookissani:

Kiinnittämätön (lupauksessa) Invariantin rikkominen: Virheellinen hook-kutsu.

Käyttö minun FunctionalComponent-komponentissani on:

const [nations, setNations] = React.useState<NationResource[]>([]);

const fetchNations = async () => {
    const loadedNations = await getNationsAsync();
    setNations(loadedNations);
};

React.useEffect(() => {
    fetchNations();
}, []);

Tiedän, että ongelma on kutsua useHook-koodia metodin sisällä, joka on välitetty useEffect-koodille, mikä on kiellettyä. Ongelma on, että en ymmärrä oikein käsitettä siitä, miten käyttää kansakuntia keskitetysti (hook jakaa tuloksen, ei logiikkaa) mutta vain ladata ne kerran komponenteissa, jotka tarvitsevat kansakuntia.

Vastaus #1

Koukkusi hallitsee valtioiden tilaa ja palauttaa sen. Sen sijaan, että käyttäisit useState, käytät useLocalStorage -hookia, joka, niin paljon kuin pystyin lukemaan lähdekoodista, käyttää alkuperäisenä tilana localStorage-arvoa tai annettua arvoa (null tapauksessasi), jos paikallista arvoa ei ole.

export const useNations = ():
    | NationResource[]
    | null => {
    const [storedNations, setStoredNations] = useLocalStorage<
        NationResource[] | null
    >("nations", null);

    useEffect(() => {
        // If storedNations has a value don't continue.
        if (storedNations) {
            return;
        }

        const fetch = async () => {
            // Check the types here, Im not sure what NationsGetResponse has.
            const nationsResponse = await Axios.get<NationsGetResponse>(
                "/v1/nations/"
            );

            setStoredNations(nationsResponse.data.nations);
        };

        fetch();
    }, []);

    return storedNations;
};

Sitten voit käyttää tätä hookia missä tahansa komponentissa:

export const MyComponent: React.ComponentType<Props> = props => {
    const nations = useNations();

    return // etc.
};

Vastaus #2

Voit lisätä asynkronisuuden mukautettuihin hookeihin. Luulen, että tämä on mitä etsit.

export function getNations(props) {
    const [storedNations, setStoredNations] = useLocalStorage("nations",[]);

    async function getNations() {
      const nationsResponse = await Axios.get("/v1/nations/");
      setStoredNations(nationsResponse.data.nations);
    }

    useEffect(() => {
      if (storedNations.length === 0) {
        getNations()
      }
    }, [])
    return storedNations;
}

Voit kutsua sitä näin

  function App() {
     const nations = getNations()
     return (
       // jsx here
     )
  }