【问题标题】:useEffect efficiency in Star Wars API星球大战 API 中的 useEffect 效率
【发布时间】:2021-09-23 19:27:22
【问题描述】:

我需要一些帮助来帮助我在 React 中进行当前项目。我正在为我的工作面试制作一个星球大战应用程序,但我遇到了一个问题。 获取效率。

我正在获取这些数据,然后再获取一些数据,因为第一个获取的数据中有 url,一切都很好,但首先我在表中看到了“url”,然后它变成了正确的数据。

当一切都正确呈现时,我想将“已获取”状态设置为 true,但我不知道该怎么做。

    const api = `https://swapi.dev/api/people/`;
    const [characters, setCharacters] = useState([]);
    const [speciesOptions, setSpeciesOptions] = useState([]);
    const [homeworldOptions, setHomeworldOptions] = useState([]);
    const [fetched, setFetched] = useState(false);

    useEffect(() => {
        const fetchedTimeout = () => {
            setTimeout(() => {
                setFetched(true);
            }, 2000);
        };

        const fetchArray = (array, arrName) => {
            for (let elem of array) {
                fetch(elem).then((response) =>
                    response.json().then((data) => {
                        array.shift();
                        array.push(data.name);
                    })
                );
            }
            if (arrName === "species") {
                if (!array.length) {
                    array.push("Unspecified");
                }
            }
        };

        async function fetchOtherData(characters) {
            await characters.forEach((character) => {
                const homeworld = character.homeworld;
                const vehicles = character.vehicles;
                const starships = character.starships;
                const species = character.species;

                fetch(homeworld).then((response) =>
                    response.json().then((data) =>
                        setCharacters((prevData) =>
                            prevData.map((prevCharacter) =>
                                prevCharacter.homeworld === homeworld
                                    ? {
                                            ...prevCharacter,
                                            homeworld: data.name,
                                      }
                                    : prevCharacter
                            )
                        )
                    )
                );

                fetchArray(vehicles);
                fetchArray(starships);
                fetchArray(species, "species");
            });
            await setCharacters(characters);
            await fetchedTimeout();
        }

        const fetchAllCharacters = (allCharacters, data) => {
            if (data.next) {
                fetch(data.next)
                    .then((response) => response.json())
                    .then((data) => {
                        allCharacters.push(...data.results);
                        fetchAllCharacters(allCharacters, data);
                    });
            }
            if (!data.next) {
                fetchOtherData(allCharacters);
            }
        };

        async function fetchApi() {
            const allCharacters = [];
            await fetch(api)
                .then((response) => response.json())
                .then((data) => {
                    allCharacters.push(...data.results);
                    fetchAllCharacters(allCharacters, data);
                })
                .catch((error) => console.log(error));
        }

        const setSpeciesFiltering = () => {
            const speciesFiltering = [];

            for (let character of characters) {
                const characterSpecies = character.species.join();
                const foundSpecies = speciesFiltering.indexOf(characterSpecies);
                if (foundSpecies === -1) {
                    speciesFiltering.push(characterSpecies);
                }
            }
            const speciesOptions = speciesFiltering.map((species) => (
                <option value={species}>{species}</option>
            ));

            setSpeciesOptions(speciesOptions);
        };

        const setHomeworldFiltering = () => {
            const homeworldFiltering = [];

            for (let character of characters) {
                const characterHomeworld = character.homeworld;
                const foundSpecies =
                    homeworldFiltering.indexOf(characterHomeworld);
                if (foundSpecies === -1) {
                    homeworldFiltering.push(characterHomeworld);
                }
            }
            const homeworldOptions = homeworldFiltering.map((homeworld) => (
                <option value={homeworld}>{homeworld}</option>
            ));

            setHomeworldOptions(homeworldOptions);
        };
        fetchApi();
        setSpeciesFiltering();
        setHomeworldFiltering();
    }, []);

感谢您的回复。

好的,在所有的 cmets 之后(谢谢!)我把代码改成了这样。

useEffect(() => {
        const fetchOtherData = (characters) => {
            const charactersWithAllData = [];
            characters.forEach((character) => {
                const homeworld = character.homeworld;
                const species = character.species;
                const vehicles = character.vehicles;
                const starships = character.starships;
                let urls = [homeworld, ...species, ...vehicles, ...starships];

                Promise.all(
                    urls.map((url) => {
                        if (url.length) {
                            fetch(url)
                                .then((response) => response.json())
                                .then((data) => {
                                    if (url.search("species") > 0) {
                                        character.species = data.name;
                                    }
                                    if (url.search("planets") > 0) {
                                        character.homeworld = data.name;
                                    }
                                    if (url.search("vehicles") > 0) {
                                        character.vehicles.shift();
                                        character.vehicles.push(data.name);
                                    }
                                    if (url.search("starships") > 0) {
                                        character.starships.shift();
                                        character.starships.push(data.name);
                                    }
                                })
                                .catch((err) => console.error(err));
                        }
                        if (!url.length) {
                            if (url.search("species")) {
                                character.species = "Unspecified";
                            }
                            if (url.search("vehicles")) {
                                character.vehicles = "";
                            }
                            if (url.search("starships")) {
                                character.starships = "";
                            }
                        }
                    })
                ).then(charactersWithAllData.push(character));
            });
            return charactersWithAllData;
        };

        const fetchApi = () => {
            const characters = [];
            Promise.all(
                [api].map((api) =>
                    fetch(api)
                        .then((response) => response.json())
                        .then((data) => characters.push(...data.results))
                        .then((data) => {
                            setCharacters(fetchOtherData(characters));
                        })
                )
            );
        };
        fetchApi();
    }, []);

我必须在什么时候设置“字符”状态?因为在上面的情况下,数据首先显示在屏幕上,然后设置状态。

【问题讨论】:

  • Promise.all 可能会有所帮助。
  • 你可以将所有的 fetch 请求映射到一个 Promises 数组和Promise.all 它们。不要将async/await 与承诺链混合。 Array.prototype.forEach 是同步的,所以你不能 await 它。 fetchArray 函数似乎改变了传递给它的数组。此外,您不应该将 JSX 存储在状态中,即物种和家庭世界选项。存储数据并通过渲染 UI 渲染选项。
  • Drew 说了什么,但我也建议您拆分您的 useEffect 钩子,每个钩子只做一件事,并让其他 useEffect 钩子具有适当的依赖关系。拥有 10 个独立的钩子,都做一件事,而不是 1 个巨大的钩子做十件事,这被认为是完全可以接受的做法。
  • 所有这些都非常有帮助,谢谢!我已经尽我所能并理解了。你也可以看看另一个问题吗? stackoverflow.com/questions/69299486/…

标签: javascript reactjs asynchronous async-await fetch


【解决方案1】:

正如其他 cmets 所说,使用 Promise.all 并重构您的 useEffect 是最好的解决方案。

但如果您不想进行太多更改,这可能会有所帮助。

(但仍然考虑重构你的钩子)

const [loading, setLoading] = useState(0);
const isLoading = loading > 0;

// replace your fetches with below:
const myFetch = async (path) => {
  try {
    setLoading(loading => loading + 1);
    return await fetch(path);
  } finally {
    setLoading(loading => loading - 1);
  }
};

useEffect(() => {
  // do your stuffs
}, []);

【讨论】:

  • const [isLoading, setIsLoading] = useState(false)setIsLoading(true|false) 会不会更简单、更直观?此外,如果loading 是一个数字,您可以使用这样一个事实,即非零值是真实的,而0 是虚假的,即!!loading
  • @DrewReese 因为他想一次执行多个提取。
  • 所有这些都非常有帮助,谢谢!我已经尽我所能并理解了。你也可以看看另一个问题吗? stackoverflow.com/questions/69299486/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-04
  • 2013-03-08
  • 1970-01-01
  • 1970-01-01
  • 2021-03-10
  • 1970-01-01
  • 2014-01-11
相关资源
最近更新 更多