关于javascript:React useEffect和异步提取

React useEffect and async fetch

这是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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 loading...
  }

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

看起来一切都很好...除非我将useEffect的第二个参数从[url]替换为[url,options]。
当存在\\'options \\'时,我们将进入众所周知的无限循环...但是将其包含在此数组中是合乎逻辑的。这怎么了
谢谢


{ method: 'GET' }定义为常量对象,以使options参数始终相同,这是一个示例:

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

否则,每次useMyFetch被调用时,options将被视为已更改,因为{ method: 'GET' } === { method: 'GET' }false


正如@Titus已经提到的,这是因为{ method: 'GET' }在每个新渲染中都存在参照差异。但是我相信,将组件移出现实生活中不够灵活。需要将令牌传递给标头或其他任何动态计算,这是预期的要求,对吧?

选项1。我们可以使用JSON.stringify将参数对象作为字符串传递:

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

选项2。我们可以使用useRef存储以前的道具并使用自定义比较:

1
2
3
4
5
6
7
function useMyFetch(url, options) {
  const prevOptions = useRef(null);
  useEffect(() => {
   ...
   prevOptions.current = options;
  }, [url, customCompare(options, prevOptions.current)])
}

选项3。最后,我可以创建自定义钩子,如果所有嵌套属性都相等,则该钩子将返回引用相同的对象:

1
2
3
4
5
6
7
8
function useObject(obj) {
  const prevObj = useRef(obj);
  const isEqual = _.isEqual(prevObj.current, obj);
  useEffect(() => {
    prevObj.current = obj;
  }, [isEqual]);
  return isEqual ? prevObj.current : obj;
}

,以后用作

1
2
3
4
5
6
7
8
9
10
11
12
function Example()
{
  const requestOptions = useObject({ method: 'GET' });
  const res = useMyFetch("https://dog.ceo/api/breeds/image/random", requestOptions);

  if (!res)
  {
    return loading...
  }

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

选项4。最直接的方法是将对象options分解为原始值:

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

我相信虽然此方法比上面的方法更为详尽,但它会稍微快一些,特别是如果rest通常为空(没有多余的标头)。


查看要测试的URL显示图像
它应该返回setResponse您想要的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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 loading...
  }

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


export default Example;