reactjs React Hooks -创建 AJAX 请求

zlhcx6iw  于 2023-05-06  发布在  React
关注(0)|答案(9)|浏览(165)

我刚刚开始玩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>
    )
}
wljmcqd8

wljmcqd81#

您可以创建一个名为useFetch的自定义钩子来实现useEffect钩子。
如果你传递一个空数组作为useEffect的第二个参数,钩子将在componentDidMount上触发请求。通过在数组中传递url,这将在url更新时触发此代码。
Here is a demo in code sandbox
请参见下面的代码。

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

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

  useEffect(() => {
    async function fetchData() {
      const response = await fetch(url);
      const json = await response.json();
      setData(json);
    }
    fetchData();
  }, [url]);

  return data;
};

const App = () => {
    const URL = 'http://www.example.json';
    const result = useFetch(URL);
 
    return (
      <div>
        {JSON.stringify(result)}
      </div>
    );
}
ugmeyewa

ugmeyewa2#

很好用...给你:

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>
  );
};

现场演示:

编辑

基于版本更改进行了更新(感谢@mgol在评论中引起我的注意)。

iovurdzv

iovurdzv3#

到目前为止都是很好的答案,但是我将添加一个自定义钩子,以便在您想要触发请求时使用,因为您也可以这样做。

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
  });
}

然后将createTodo指向onSubmitonClick处理程序。newTodo会有你的数据,加载和错误状态。沙盒代码right here.

yebdmbv4

yebdmbv44#

use-http是一个有点ReactuseFetch钩子,使用如下:https://use-http.com

import useFetch from 'use-http'

function Todos() {
  const [todos, setTodos] = useState([])
  const { request, response } = useFetch('https://example.com')

  // componentDidMount
  useEffect(() => { initializeTodos() }, [])

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

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

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

或者,如果你不想自己管理国家,你可以

function Todos() {
  // the dependency array at the end means `onMount` (GET by default)
  const { loading, error, data } = useFetch('/todos', [])

  return (
    <>
      {error && 'Error!'}
      {loading && 'Loading...'}
      {data && data.map(todo => (
        <div key={todo.id}>{todo.title}</div>
      )}
    </>
  )
}

现场演示

wqlqzqxt

wqlqzqxt5#

我建议你使用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}
    >  
  )
}

image example
作者免责声明:我们已经在生产中使用了这个实现。有一堆钩子来处理promise,但也有边缘情况没有被覆盖或没有足够的测试实现。react-request-hook在正式发布之前就已经经过了实战测试。它的主要目标是经过良好的测试和安全使用,因为我们正在处理我们的应用程序最关键的方面之一。

syqv5f0l

syqv5f0l6#

传统上,您将在类组件的componentDidMount生命周期中编写 AJAX 调用,并在请求返回时使用setState显示返回的数据。
对于钩子,可以使用useEffect并传入一个空数组作为第二个参数,使回调在组件挂载时运行一次。
下面是一个从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/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>
eoxn13cs

eoxn13cs7#

我在上面的答案中发现了很多useEffect的错误用法。
异步函数不应该传入useEffect
让我们看看useEffect的签名:

useEffect(didUpdate, inputs);

你可以在didUpdate函数中做副作用,并返回一个dispose函数。dispose函数非常重要,你可以用它来取消一个请求,清除一个计时器等等。
任何async函数都会返回一个promise,而不是一个函数,所以dispose函数实际上没有任何效果。
所以传入一个异步函数绝对可以处理你的副作用,但它是Hooks API的反模式。

mwkjh3gx

mwkjh3gx8#

我觉得有个办法行得通

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

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

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

        setData(data);
      }
      getData();
    }, []);

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

有几个重要的部分:

  • 传递给useEffect的函数充当componentDidMount,这意味着它可以执行多次。这就是为什么我们要添加一个空数组作为第二个参数,这意味着“这个效果没有依赖关系,所以只运行一次”。
  • 您的App组件仍然呈现一些东西,即使数据还没有在这里。因此,您必须处理未加载数据但呈现组件的情况。顺便说一句,这一点没有改变。我们现在也在这样做。
k7fdbhmy

k7fdbhmy9#

这里也许是对公认答案的稍微改进。(包括错误处理和传递要获取的选项)

const useFetch = (url, options = {}) => {
  const [response, setResponse] = React.useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(url, options)
      .then(res => res.json())
      .then(setResponse)
      .catch(setError);
  }, [url, ...Object.values(options)]);

  return {response, error};
};

相关问题