如何将 Fetch 转换为 Axios,将 Class 组件转换为 Functional 组件?

How to convert Fetch to Axios and Class component to Functional component?

如何将 Fetch 转换为 Axios,将 Class 组件转换为 Functional 组件?

我想学习在React native中使用函数式组件和axios实现Infinite-Scrolling,但是很难应用,因为参考文档是由class组件和fetch组成。

import React from 'react';
import {
  View,
  Image,
  Text,
    FlatList, // here
} from 'react-native';

export default class App extends React.Component {

  state = {
    data: [],
    page: 1 // here
  }

  _renderItem = ({item}) => (
    <View style={{borderBottomWidth:1, marginTop: 20}}>
      <Image source={{ uri: item.url }} style={{ height: 200}} />
      <Text>{item.title}</Text>
      <Text>{item.id}</Text>
    </View>
  );

  
  _getData = () => {
    const url = 'https://jsonplaceholder.typicode.com/photos?_limit=10&_page=' + this.state.page;
    fetch(url)
      .then(r => r.json())
      .then(data => {
        this.setState({ 
          data: this.state.data.concat(data),
          page: this.state.page + 1
        })
      });
  }

  componentDidMount() {
    this._getData();
  }

  // here
  _handleLoadMore = () => {
    this._getData();
  }

  render() {
    return (
      <FlatList 
        data={this.state.data}
        renderItem={this._renderItem}
        keyExtractor={(item, index) => item.id}
        onEndReached={this._handleLoadMore}
        onEndReachedThreshold={1}
      />
    );
  }
}

从 class 转换为函数组件时,这里有几个相关步骤:

  • componentDidMount 等生命周期事件替换为 useEffect
  • 用一个或多个 useState 钩子替换组件状态。
  • 将 class 方法转换为普通函数。
  • 删除对 this.
  • 的所有引用
  • 删除 render() 并直接 return JSX。

方法_renderItem_getData_handleLoadMore基本不变。它们只是成为 const 变量而不是 class 属性。

这是从 class 到函数组件的直接转换:

import React, { useEffect, useState } from 'react';
import {
  View,
  Image,
  Text,
  FlatList,
} from 'react-native';

export default function App() {
  const [page, setPage] = useState(1);
  const [data, setData] = useState([]);

  const _renderItem = ({ item }) => (
    <View style={{ borderBottomWidth: 1, marginTop: 20 }}>
      <Image source={{ uri: item.url }} style={{ height: 200 }} />
      <Text>{item.title}</Text>
      <Text>{item.id}</Text>
    </View>
  );

  const _getData = () => {
    const url =
      'https://jsonplaceholder.typicode.com/photos?_limit=10&_page=' + page;
    fetch(url)
      .then((r) => r.json())
      .then((data) => {
        setData(data.concat(data));
        setPage(page + 1);
      });
  };

  const _handleLoadMore = () => {
    _getData();
  };

  // useEffect with an empty dependency array replaces componentDidMount()
  useEffect(() => _getData(), []);

  return (
    <FlatList
      data={data}
      renderItem={_renderItem}
      keyExtractor={(item, index) => item.id}
      onEndReached={_handleLoadMore}
      onEndReachedThreshold={1}
    />
  );
}

这是 axios 以及其他一些改进。我注意到在到达初始零长度列表的末尾时调用了 end reached 函数,导致第一页被获取两次。所以实际上不需要 componentDidMount 。我从.then()改成了async/await,不过没关系。

import React, { useEffect, useState } from 'react';
import { View, Image, Text, FlatList } from 'react-native';
import axios from 'axios';

export default function App() {
  const [page, setPage] = useState(1);
  const [data, setData] = useState([]);

  const _renderItem = ({ item }) => (
    <View style={{ borderBottomWidth: 1, marginTop: 20 }}>
      <Image source={{ uri: item.url }} style={{ height: 200 }} />
      <Text>{item.title}</Text>
      <Text>{item.id}</Text>
    </View>
  );

  const _getData = async () => {
    const url =
      'https://jsonplaceholder.typicode.com/photos?_limit=10&_page=' + page;
    const res = await axios.get(url);
    setData(data.concat(res.data));
    setPage(page + 1);
  };

  const _handleLoadMore = () => {
    _getData();
  };

  // useEffect with an empty dependency array replaces componentDidMount()
  useEffect(() => {
    // put async functions inside curly braces to that you aren't returing the Promise
    _getData();
  }, []);

  return (
    <FlatList
      data={data}
      renderItem={_renderItem}
      keyExtractor={(item, index) => item.id}
      onEndReached={_handleLoadMore}
      onEndReachedThreshold={1}
    />
  );
}

Expo Link -- 它在我的设备上有效,但无限滚动在网络预览中似乎不起作用。

您还可以进行更多“高级”改进:

  • 使用先前状态的回调设置状态以确保值始终正确。 setPage(current => current + 1)setData(current => current.concat(res.data))
  • 使用 useCallback 进行记忆,以便像 _renderItem 这样的函数在重新渲染时保持恒定的引用。
  • 详尽的 useEffect 依赖项(需要记忆)。