web-dev-qa-db-ja.com

Reactフック-Ajaxリクエストを行う

私はReactフックで遊んで始めたばかりで、AJAXリクエストはどのように見えるのでしょうか?

私は多くの試みを試みましたが、それを機能させることができず、実際にそれを実装する最良の方法も知りません。以下は私の最新の試みです。

import React, { useState, useEffect } from 'react';

const App = () => {
    const URL = 'http://api.com';
    const [data, setData] = useState({});

    useEffect(() => {
        const resp = fetch(URL).then(res => {
          console.log(res)
        });
    });

    return (
        <div>
          // display content here
        </div>
    )
}
11
peter flanagan

useFetchフックを実装するuseEffectというカスタムフックを作成できます。

useEffectフックの2番目の引数として空の配列を渡すと、componentDidMountでリクエストがトリガーされます。

コードサンドボックスのデモ

以下のコードを参照してください。

import React, { useState, useEffect } from 'react';

const useFetch = (url) => {
  const [data, updateData] = useState(undefined);

  // empty array as second argument equivalent to componentDidMount
  useEffect(() => {
    async function fetchData() {
      const response = await fetch(url);
      const json = await response.json();
      updateData(json);
    }
    fetchData();
  }, [url]);

  return data;
};

const App = () => {
    const URL = 'http://www.example.json';
    const result = useFetch(URL);

    return (
      <div>
        {JSON.stringify(result)}
      </div>
    );
}
23
Paul Fitzgerald

これまでのところ素晴らしい回答ですが、リクエストをトリガーするタイミングのためにカスタムフックを追加します。

function useTriggerableEndpoint(fn) {
  const [res, setRes] = useState({ data: null, error: null, loading: null });
  const [req, setReq] = useState();

  useEffect(
    async () => {
      if (!req) return;
      try {
        setRes({ data: null, error: null, loading: true });
        const { data } = await axios(req);
        setRes({ data, error: null, loading: false });
      } catch (error) {
        setRes({ data: null, error, loading: false });
      }
    },
    [req]
  );

  return [res, (...args) => setReq(fn(...args))];
}

必要に応じて、特定のAPIメソッドに対してこのフックを使用して関数を作成できますが、この抽象化は厳密には必要ではなく、非常に危険な可能性があることに注意してください(フックを持つ緩い関数は、 Reactコンポーネント関数)のコンテキスト外で使用されます。

const todosApi = "https://jsonplaceholder.typicode.com/todos";

function postTodoEndpoint() {
  return useTriggerableEndpoint(data => ({
    url: todosApi,
    method: "POST",
    data
  }));
}

最後に、関数コンポーネント内から

const [newTodo, postNewTodo] = postTodoEndpoint();

function createTodo(title, body, userId) {
  postNewTodo({
    title,
    body,
    userId
  });
}

そして、createTodoonSubmitまたはonClickハンドラーを指すようにします。 newTodoには、データ、ロード、およびエラーのステータスが含まれます。サンドボックスコード ここ

7
horyd

うまく動作します...ここに行きます:

編集:バージョンの変更に基づいて更新されました(コメントで私の注意を喚起してくれた@mgolに感謝...

import React, { useState, useEffect } from 'react';

const useFetch = url => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  const fetchUser = async () => {
    const response = await fetch(url);
    const data = await response.json();
    const [user] = data.results;
    setData(user);
    setLoading(false);
  };

  useEffect(() => {
    fetchUser();
  }, []);

  return { data, loading };
};

const App = () => {
  const { data, loading } = useFetch('https://api.randomuser.me/');

  return (
    <div className="App">
      {loading ? (
        <div>Loading...</div>
      ) : (
        <React.Fragment>
          <div className="name">
            {data.name.first} {data.name.last}
          </div>
          <img className="cropper" src={data.picture.large} alt="avatar" />
        </React.Fragment>
      )}
    </div>
  );
};

ライブデモ:

Edit x908rkw8yq

6
SakoBu

react-request-hook を使用することをお勧めします。多くのユースケース(同時に複数の要求、アンマウントのキャンセル可能な要求、管理された要求の状態)をカバーするためです。 TypeScriptで記述されているので、プロジェクトでTypeScriptを使用している場合はこれを利用でき、使用しない場合はIDEに応じて、タイプヒントが表示され、ライブラリには、リクエストの結果として期待されるペイロードを安全に入力できるようにするヘルパーも用意されています。

十分にテストされており(100%のコードカバレッジ)、次のように簡単に使用できます。

function UserProfile(props) {
  const [user, getUser] = useResource((id) => {
    url: `/user/${id}`,
    method: 'GET'
  })

  useEffect(() => getUser(props.userId), []);

  if (user.isLoading) return <Spinner />;
  return (
    <User 
      name={user.data.name}
      age={user.data.age}
      email={user.data.email}
    >  
  )
}

画像の例

著者の免責事項:本番環境ではこの実装を使用しています。約束を処理するためのフックがたくさんありますが、カバーされていないEdgeケースや十分なテストが実装されていないケースもあります。 react-request-hookは、公式リリース前でもバトルテスト済みです。その主な目標は、アプリの最も重要な側面の1つを扱っているため、十分にテストされ、安全に使用できるようにすることです。

2

これがうまくいくと思うものです:

import React, { useState, useEffect } from 'react';

const App = () => {
    const URL = 'http://api.com';
    const [data, setData] = useState({})

    useEffect(async () => {
      const resp = await fetch(URL);
      const data = await resp.json();

      setData(data);
    }, []);

    return (
      <div>
        { data.something ? data.something : 'still loading' }
      </div>
    )
}

重要な点がいくつかあります。

  • useEffectに渡す関数はcomponentDidMountとして機能します。つまり、何度も実行される可能性があります。これが、空の配列を2番目の引数として追加する理由です。つまり、「このエフェクトには依存関係がないため、1回だけ実行する」ということです。
  • Appコンポーネントは、データがまだ存在しない場合でも何かをレンダリングします。そのため、データはロードされず、コンポーネントがレンダリングされるケースを処理する必要があります。ちなみにその点に変更はありません。今でもそうしています。
1
Krasimir

従来、クラスコンポーネントのcomponentDidMountライフサイクルでAjax呼び出しを記述し、リクエストが返されたときにsetStateを使用して返されたデータを表示していました。

フックでは、useEffectを使用し、2番目の引数として空の配列を渡して、コンポーネントのマウント時にコールバックを1回実行します。

APIからランダムなユーザープロファイルを取得し、名前をレンダリングする例を次に示します。

function AjaxExample() {
  const [user, setUser] = React.useState(null);
  React.useEffect(() => {
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        setUser(data.results[0]);
      });
  }, []); // Pass empty array to only run once on mount.
  
  return <div>
    {user ? user.name.first : 'Loading...'}
  </div>;
}

ReactDOM.render(<AjaxExample/>, document.getElementById('app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>

<div id="app"></div>
0
Yangshun Tay

上記の回答でuseEffectの間違った使い方をたくさん見つけました。

非同期関数はuseEffectに渡すべきではありません。

useEffectの署名を見てみましょう:

useEffect(didUpdate, inputs);

didUpdate関数で副作用を発生させ、処分関数を返​​すことができます。破棄関数は非常に重要です。その関数を使用して、リクエストをキャンセルしたり、タイマーをクリアしたりできます。

非同期関数は、promiseを返しますが、関数は返しません。したがって、dispose関数は実際には効果を持ちません。

そのため、非同期関数を渡すことは絶対に副作用を処理できますが、フックAPIのアンチパターンです。

0
李元秋

se-http は、次のように使用される少し反応するuseFetchフックです。 https://use-http.com

function Todos() {
  const [todos, setTodos] = useState([])

  const request = useFetch('https://example.com')

  // on mount, initialize the todos
  useEffect(() => {
    initializeTodos()
  }, [])

  async function initializeTodos() {
    const initialTodos = await request.get('/todos')
    setTodos(initialTodos)
  }

  async function addTodo() {
    const newTodo = await request.post('/todos', {
      title: 'no way',
    })
    setTodos(oldTodos => [...oldTodos, newTodo])
  }

  return (
    <>
      <button onClick={ addTodo }>Add Todo</button>
      {todos.error && 'Error!'}
      {todos.loading && 'Loading...'}
      {todos.length > 0 && todos.map(todo => (
        <div key={todo.id}>{todo.title}</div>
      )}
    </>
  )
}
0
Alex Cory