React: hooks 该怎么优雅的获取数据

3,686 阅读2分钟

使用 react hooks 优雅的获取数据

写在最前面

  • 适用于 react,es6使用者,react hooks 初学者。
  • 本文主要写关于怎么使用 stateeffect hooks 来优雅的获取列表数据。
    • 怎么定制一个获取数据的 hook?
  • 当然你需要先了解一下 react hooks 的新特性

使用 hook 获取数据

1、useState的使用

import React, { useState } from 'react';

function App() {
  const [data, setData] = useState({ hits: [] });

  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default App;

state, hooks 主动去回调 userState 方法,把 data 存储在 state 中。

2、Axios 的使用(useEffect的使用)

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

function App() {
  const [data, setData] = useState({ hits: [] });

  useEffect(async () => {
    const result = await axios(
      'http://hn.algolia.com/api/v1/search?query=redux',
    );

    // 使用 useEffect  的时候,我们主动设置 `state` ,存储 `setData`
    setData(result.data);
  });

  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default App;

当你运行上面的程序的时候会发现有 bug ,这是为什么啦?因为当我们在获取数据后存储数据到 state 中的时候,我们的组件会随之更新,然后 effect 会再次运行一次。然后我们会又获取一次 data

  • Warning: useEffect function must return a cleanup function or nothing. Promises and useEffect(async () => …) are not supported, but you can call an async function inside an effect..

  • 怎么避免上面的问题啦?

  • 我们的目的是只在组件加载完成的时候获取数据

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

function App() {
  const [data, setData] = useState({ hits: [] });

  // 单独拆分 fetchData
  const fetchData = async () => {
    const result = await axios(
      'http://hn.algolia.com/api/v1/search?query=redux',
    );

    setData(result.data);
  };
  // 单独拆分 fetchData 的原因是: 上面的 waring 部分,不推荐把 async 写在 effect 中

  useEffect(() => {
    fetchData();
  }, []);
  // 这里的第二个参数: 是 hooks 来观测数值的变化
  // 这里添加 [],当我们的组件更新的时候回去观测 effect 的值是否有变化,这里添加空 [] ,是为了防止 hooks 再一次运行。

  return (
    <ul>
      {data.hits.map(item => (
        <li key={item.objectID}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default App;

3、 Form 表单的时候怎么使用?

  • 使用 Form 的时候
function App() {
  ...

  // In order to prevent the default behavior,为了防止form 的默认行为,我们建议单独把 doGet 方法拆分出来写。而不是直接写在 form
  的 onchange 方法中
  const doGet = event => {
    setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`);
    event.preventDefault();
  };

  return (
    <Fragment>
      <form onSubmit={doGet}>
        <input
          type="text"
          value={query}
          onChange={event => setQuery(event.target.value)}
        />
        <button type="submit">Search</button>
      </form>

      {isError && <div>Something went wrong ...</div>}

      ...
    </Fragment>
  );
}

4、怎么定制一个数据获取的 hook?

  • 我们当然可以根据自己的情况定制一个 hook 来获取我们的数据,这里需要处理 loading ,error 情况,数据来源等。
// 做一个新闻的数据获取的 API
const useHackerNewsApi = () => {
  const [data, setData] = useState({ hits: [] });
  const [url, setUrl] = useState(
    'http://hn.algolia.com/api/v1/search?query=redux',
  );
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  const fetchData = async () => {
    setIsError(false);
    setIsLoading(true);

    try {
      const result = await axios(url);

      setData(result.data);
    } catch (error) {
      setIsError(true);
    }

    setIsLoading(false);
  };

  useEffect(() => {
    fetchData();
  }, [url]);

  const doGet = event => {
    setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`);
    event.preventDefault();
  };

  return { data, isLoading, isError, doGet };
}
  • 怎么使用
// 直接调用即可
function App() {
  const [query, setQuery] = useState('redux');
  const { data, isLoading, isError, doGet } = useHackerNewsApi();

  return (
    <Fragment>
      ...
    </Fragment>
  );
}

进一步的定制 APIurl

const useHackerNewsApi = () => {
  ...

  useEffect(
    ...
  );

  const doGet = (event, url) => {
    setUrl(url);
    event.preventDefault();
  };

  return { data, isLoading, isError, doGet };
};

function App() {
  const [query, setQuery] = useState('redux');
  const { data, isLoading, isError, doGet } = useHackerNewsApi();

  return (
    <Fragment>
      <form
        onSubmit={event =>
          doGet(
            event,
            `http://hn.algolia.com/api/v1/search?query=${query}`,
          )
        }
      >
        <input
          type="text"
          value={query}
          onChange={event => setQuery(event.target.value)}
        />
        <button type="submit">Search</button>
      </form>

      ...
    </Fragment>
  );
}

定制 state 的 初始化data

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

// 定制 initialUrl 和 initialData
const useDataApi = (initialUrl, initialData) => {
  const [data, setData] = useState(initialData);
  const [url, setUrl] = useState(initialUrl);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  const fetchData = async () => {
    setIsError(false);
    setIsLoading(true);

    try {
      const result = await axios(url);

      setData(result.data);
    } catch (error) {
      setIsError(true);
    }

    setIsLoading(false);
  };

  useEffect(() => {
    fetchData();
  }, [url]);

  const doGet = (event, url) => {
    setUrl(url);
    event.preventDefault();
  };

  return { data, isLoading, isError, doGet };
};

function App() {
  const [query, setQuery] = useState('redux');
  const { data, isLoading, isError, doGet } = useDataApi(
    'http://hn.algolia.com/api/v1/search?query=redux',
    { hits: [] },
  );

  return (
    <Fragment>
      <form
        onSubmit={event =>
          doGet(
            event,
            `http://hn.algolia.com/api/v1/search?query=${query}`,
          )
        }
      >
        <input
          type="text"
          value={query}
          onChange={event => setQuery(event.target.value)}
        />
        <button type="submit">Search</button>
      </form>

      {isError && <div>Something went wrong ...</div>}

      {isLoading ? (
        <div>Loading ...</div>
      ) : (
        <ul>
          {data.hits.map(item => (
            <li key={item.objectID}>
              <a href={item.url}>{item.title}</a>
            </li>
          ))}
        </ul>
      )}
    </Fragment>
  );
}

export default App;

好了现在我们可以优雅地使用 hooks 去获取我们的数据了

参考