Невозможно получить данные из API в компоненте

Я работаю над этим проектом в React JS, где я получаю данные из этого URL-адреса API для моей разработки интерфейса. .

Я сделал свои собственные хуки для извлечения данных в несколько файлов после эта средняя статья следующим образом:

useApiResult.js

import { useState, useEffect } from "react";

export const useApiResult = (request) => {
  const [results, setResults] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(request)
      .then(async (response) => {
        if (response.ok) {
          setResults(await response.json());
          setError(null);
        } else {
          setError(await response.text())
        }
      })
      .catch((err) => {
        setError(err.message);
      });
  }, [request]);

  return [results, error];
};

useImages.js

import { useMemo } from "react";
import { useApiResult } from "./useApiResult";

const BASE_URL = "http://api.vidyarajkumari.com";
const createUrl = (base, path) => `${base}${path}`;

const getImages = () => [
  createUrl(BASE_URL, "/images/"),
  {
      method: "GET",
  }
];

export const useImages = () => {
  const request = useMemo(() => getImages(), []);
  return useApiResult(request);
}

Компонент React: Images.js

import React from "react";
import { useImages } from "../../hooks/useImages";

export default function Images() {
  const [images, error] = useImages();
  //console.log(images);
  //console.log(error);
  return (
    <>
      <div className="row">
        {
          images.map((item, index) => {
            <div key={index} className="col-md-4 animate-box">
            ...
            // Rest of code goes here
          }
        }
      </>
    </>
  )
}

Проблема в том, что я не могу получить данные в компоненте Images.js с помощью ловушки useImages. console.log значения изображений возвращают null. Это меня уже некоторое время беспокоит, и я был бы очень признателен за решение этой проблемы. Что я здесь делаю не так и как я могу это обойти?

P.S. URL-адрес API активен; так что не стесняйтесь ссылаться на него. Спасибо за уделенное время.


person dEBA M    schedule 05.01.2021    source источник


Ответы (2)


У меня есть лучший способ сделать это с помощью useReducer и настраиваемого крючка, проверьте это:

Кстати, я думаю, что у вашего URL-адреса API есть проблемы! (Я добавил ввод для получения другого URL-адреса для тестирования)

const IMAGE_URL = "http://api.vidyarajkumari.com/images/";

const initialState = { loading: true };

function fetchReducer(state, action) {
  switch (action.type) {
    case "fetch":
      return {
        ...state,
        error: undefined,
        loading: true,
      };

    case "data":
      return {
        ...state,
        data: action.data,
        loading: false,
      };

    case "error":
      return {
        ...state,
        error: "Error fetching data. Try again",
        loading: false,
      };

    default:
      return state;
  }
}

function useFetch(url) {
  const [state, dispatch] = React.useReducer(fetchReducer, initialState);
  
  React.useEffect(() => {
    dispatch({ type: "fetch" });

    fetch(url, {
      headers: {
        accept: "application/json",
      },
    })
      .then((res) => res.json())
      .then((data) => dispatch({ type: "data", data }))
      .catch((e) => {
        console.warn(e.message);
        dispatch({ type: "error" });
      });
  }, [url]);

  return {
    loading: state.loading,
    data: state.data,
    error: state.error,
  };
}

function FetchComponent({url}) {
  const { loading, data, error } = useFetch(url);
  console.log(data);
  if (loading) {
    return <p>Fetching {url}...</p>;
  }
  if (error) {
    return <p>{error}</p>
  }
  return <div>{JSON.stringify(data)}</div>
}

const App = () => {
  const [url, setUlr] = React.useState(IMAGE_URL)
  const inputEl = React.useRef(null);
  const changeUrl = () => setUlr(inputEl.current.value)
  return (
    <React.Fragment>
      <input defaultValue="https://icanhazdadjoke.com/" ref={inputEl} type="text" />
      <button onClick={changeUrl}>Fetch</button>
      {url && <FetchComponent url={url}/>}
    </React.Fragment>
  )
}

ReactDOM.render(<App/>, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

person b3hr4d    schedule 05.01.2021
comment
Я думаю, что этот подход выглядит чище. Не могли бы вы уточнить, какие проблемы вы имеете в виду в связи с URL-адресом API? - person dEBA M; 05.01.2021
comment
Я думаю, вы неправильно используете крючки useImages(), проверьте мой крючок, и вы это обнаружите. - person b3hr4d; 05.01.2021
comment
также проверьте мой другой ответ stackoverflow.com/a/65224984/12608714, вы можете лучше понять его. - person b3hr4d; 05.01.2021
comment
API был написан другом, поэтому, к сожалению, я не могу это контролировать. Есть ли способ поработать с этим, используя свои хуки, чтобы заставить его работать, если это не требует многого? - person dEBA M; 05.01.2021
comment
Вы имеете в виду, что я поменяю ваш бэкэнд? или вы хотите, чтобы ваш пример работал? - person b3hr4d; 05.01.2021
comment
Нет. Я спрашивал, можете ли вы изучить URL-адрес и посмотреть, можно ли сделать так, чтобы он возвращал данные вместо ошибки из вашего хука useFetch. Потому что я не могу этого сделать, но я пытаюсь. - person dEBA M; 05.01.2021
comment
Я сделал ввод внутри своего примера, вы можете попробовать другой URL-адрес и увидеть результат! - person b3hr4d; 05.01.2021
comment
У меня есть. Тем не менее, спасибо за советы. Я постараюсь найти способ обойти это. - person dEBA M; 05.01.2021
comment
Я нашел правильный URL, попробуйте это внутри ввода: api.vidyarajkumari.com/images/?format= json - person b3hr4d; 05.01.2021
comment
Спасибо еще раз за помощь. - person dEBA M; 05.01.2021

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

import { useState, useEffect } from "react";

export const useApiResult = (request) => {
  const [results, setResults] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(request)
      .then(async (response) => {
        if (response.ok) {
          setResults(await response.json());
          setError(null);
        } else {
          setError(await response.text())
        }
      })
      .catch((err) => {
        setError(err.message);
      });
  }, [request, results, error]);

  return [results, error];
};

person Muhammad Jaan    schedule 05.01.2021
comment
Принято к сведению. Но я думаю, что это скорее улучшение, чем решение моей проблемы. Но оцените предложение. - person dEBA M; 05.01.2021