Реагировать useEffect и асинхронная выборка - PullRequest
1 голос
/ 29 сентября 2019

Вот мой код:


const useMyFetch = (url, options) => 
{
  const [response, setResponse]   = React.useState(null);

  React.useEffect(() => 
  {
    console.log("going to fetch ", url);

    fetch(url, options).then(async function(response) 
    {
      var json = await response.json();
      setResponse(json.message);
    });

  }, [ url ]); 

  return response;
};



function Example() 
{
  const res = useMyFetch("https://dog.ceo/api/breeds/image/random", { method: 'GET' });

  if (!res) 
  {
    return <div>loading...</div>
  }

  return <img src={res} alt="an image" />;
}


Похоже, что все в порядке ... за исключением случаев, когда я заменяю второй аргумент useEffect с [url] на [url, options].Когда есть «опции», мы входим в хорошо известный бесконечный цикл ... однако логично иметь его в этом массиве.Что здесь не так?Спасибо

Ответы [ 3 ]

1 голос
/ 29 сентября 2019

Определите { method: 'GET' } как постоянный объект, чтобы параметр options всегда был одинаковым, вот пример:

const options = { method: 'GET' };
function Example() {
  const res = useMyFetch("https://dog.ceo/api/breeds/image/random", options);
  ...
}

В противном случае options будет считаться измененным каждый разuseMyFetch вызывается, потому что { method: 'GET' } === { method: 'GET' } - это false.

0 голосов
/ 29 сентября 2019

Как уже упоминалось @Titus, это потому, что { method: 'GET' } референциально отличается при каждом новом рендере.Но я считаю, что удаление этого компонента недостаточно гибко в реальной жизни.Необходимость передачи токена в заголовки или любое другое динамическое вычисление является довольно ожидаемым требованием, верно?

Вариант 1. Мы можем использовать JSON.stringify для передачи объекта параметра в виде строки:

function useMyFetch(url, options) {
  useEffect(() => {
  }, [url, JSON.stringify(options)])
} 

Вариант 2. Мы можем использовать useRef до для сохранения предыдущих реквизитов и использовать пользовательское сравнение:

function useMyFetch(url, options) {
  const prevOptions = useRef(null);
  useEffect(() => {
   ...
   prevOptions.current = options;
  }, [url, customCompare(options, prevOptions.current)])
} 

Вариант 3. И, наконец, я могу создать пользовательский хук, который будет возвращать один и тот же объект, если все вложенные свойства равны:

function useObject(obj) {
  const prevObj = useRef(obj);
  const isEqual = _.isEqual(prevObj.current, obj);
  useEffect(() => {
    prevObj.current = obj;
  }, [isEqual]);
  return isEqual ? prevObj.current : obj;
}

, и использовать его позже как

function Example() 
{
  const requestOptions = useObject({ method: 'GET' });
  const res = useMyFetch("https://dog.ceo/api/breeds/image/random", requestOptions);

  if (!res) 
  {
    return <div>loading...</div>
  }

  return <img src={res} alt="an image" />;
}

.Вариант 4. Самый простой способ - разложить объект options на примитивные значения:

function useMyFetch(url, options) {
  const {method, headers: {contentType} = {} , ...rest } = options;
  useEffect(() => {
  }, [url, method, contentType, JSON.stringify(rest)]);
}

Я полагаю, что хотя этот метод более многословен, чем другие, он немного быстрее, особенно если rest обычнопусто (без дополнительных заголовков).

0 голосов
/ 29 сентября 2019

посмотрите URL для тестирования покажите изображение оно должно вернуть setResponse что вы хотите

const useMyFetch = (url, options) => 
{
  const [response, setResponse]   = React.useState(null);

  React.useEffect(() => 
  {
    console.log("going to fetch ", url);

    fetch(url, options).then(async (response)=> 
    {
      var json = await response.json();
       return setResponse(json.message);
    });

  }, [ url ]); 

  return response;
};



function Example() 
{
  const res = useMyFetch("https://dog.ceo/api/breeds/image/random", { method: 'GET' });

  if (!res) 
  {
    return <div>loading...</div>
  }

  return <img src={res} alt="an image" />;
}


export default Example;
...