Как заставить re-render крюка от родительского компонента в реагировать? - PullRequest
0 голосов
/ 19 апреля 2019

У меня есть два файла:

  1. App.js
  2. useFetch.js

useFetch.js - это пользовательский хук, который я хочу использовать для выполнения запросов к определенному API с использованием fetch.

Это содержимое файла App.js:

import React, { useState } from 'react';
import useFetch from './hooks/useFetch';
...
const App = () => {
  const [page, setPage] = useState(1);
  const [data, error, setUrl, isLoading, abort] = useFetch();

  const handleAction = () => {
    if (isLoading) {
      abort();
      console.log('abort signal sended');
    } else {
      setUrl(`https://reqres.in/api/users?page=${page}`);
    }
  };

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <div
          className="App-header-button"
        >
          <Button
            variant="contained"
            color={isLoading ? 'secondary' : 'primary'}
            onClick={() => handleAction()}
          >
            {isLoading ? 'Cancel' : 'Test'}
          </Button>
        </div>
        {data && 'Data loaded'}
        <input type="text" onChange={event => setPage(event.target.value)} value={page} />
      </header>
    </div>
  );
};

export default App;

Как видите, это простое приложение. Ввод в конце используется для настройки отображения страницы из API.

Это содержимое моего пользовательского useFetch.js:

import { useState, useEffect } from 'react';

  const useFetch = (defaultUrl) => {
  const [url, setUrl] = useState(defaultUrl);
  const [data, setData] = useState();
  const [error, setError] = useState();
  const [isLoading, setIsLoading] = useState(false);
  const [controller] = useState(new AbortController());

  const { signal } = controller;

  useEffect(() => {
    if (url) {
      setIsLoading(true);
      fetch(url, { signal })
        .then(res => res.json())
        .then((res) => {
          setData(res.data);
          setIsLoading(false);
        })
        .catch((err) => {
          setError(err);
          console.log(err.name);
          setIsLoading(false);
        });
    }
  }, [url]);

  const abort = () => {
    controller.abort();
    setIsLoading(false);
  };

  return [
    data,
    error,
    setUrl,
    isLoading,
    abort,
  ];
};

export default useFetch;

У меня есть одна проблема ... она работает очень хорошо и отображает данные, когда я меняю страницу и нажимаю кнопку. Проблема заключается в следующем: если я нажимаю кнопку со страницей и снова нажимаю кнопку с той же страницей, ничего не происходит. Это имеет определенный смысл, потому что URL-адрес не меняется, все равно будут те же данные, отправленные на крючок. Предположим, пользователь хочет обновить данные той же страницей, снова нажав кнопку. В этом случае мне нужно, чтобы ловушка снова сделала тот же вызов, повторяя тот же URL. Итак, как я могу сделать так, чтобы крюк снова «рендерил» его?

[ОБНОВЛЕНИЕ I]

С помощью @Atul я внес эти изменения и работает. Мне только интересно, является ли это хорошим способом или наиболее подходящим способом достижения этого.

На useFetch.js:

  useEffect(() => {
    if (url && refreshFlag) {
      setIsLoading(true);
      fetch(url, { signal })
        .then(res => res.json())
        .then((res) => {
          setData(res.data);
          setIsLoading(false);
          setRefreshFlag(false);
        })
        .catch((err) => {
          setError(err);
          console.log(err.name);
          setRefreshFlag(false);
          setIsLoading(false);
        });
    }
  }, [url, refreshFlag]);

В App.js: (отображается только код действия кнопки)

  const handleAction = () => {
    if (isLoading) {
      abort();
      console.log('abort signal sended');
    } else {
      refresh(true);
      setUrl(`https://reqres.in/api/users?page=${page}`);
    }
  };
...