使用 axios 调用 API 时如何防止 UI 冻结

P-R*_*ens 3 reactjs react-native redux redux-thunk

当我的组件使用componentDidMount. 但是调用 Redux 操作,使用 axios 进行调用似乎冻结了 UI。当我有一个包含 12 个输入的表单并且其中一个进行 API 调用时,我会假设我可以输入其他输入并且不会让它们冻结在我身上。

我尝试阅读有关该主题的其他一些帖子,但它们都有点不同,而且我尝试的所有内容似乎都无法解决问题。

我正在使用 React 16.8 在 linux 上工作(使用 RN 时我使用 55.4)

我已经尝试使我的componentDidMount异步以及 redux-thunk 动作。它似乎没有任何帮助,所以我一定是做错了什么。

我尝试执行以下操作但没有成功。只是使用我尝试过的简短形式。下面列出了实际代码。

async componentDidMount() {
    await getTasks().then();
}
Run Code Online (Sandbox Code Playgroud)

我试过这个

export const getTasks = () => (async (dispatch, getState) => {
    return await axios.get(`${URL}`, AJAX_CONFIG).then();
}
Run Code Online (Sandbox Code Playgroud)

当前代码:

组件.js

componentDidMount() {
    const { userIntegrationSettings, getTasks } = this.props;
    // Sync our list of external API tasks
    if (!isEmpty(userIntegrationSettings)) {
        getTasks(userIntegrationSettings.token)
            // After we fetch our data from the API create a mapping we can use
            .then((tasks) => {
                Object.entries(tasks).forEach(([key, value]) => {
                    Object.assign(taskIdMapping, { [value.taskIdHuman]: key });
                });
            });
    }
}

Run Code Online (Sandbox Code Playgroud)

动作.js

export const getTasks = () => ((dispatch, getState) => {
    const state = getState();
    const { token } = state.integrations;

    const URL = `${BASE_URL}/issues?fields=id,idReadable,summary,description`;
    const AJAX_CONFIG = getAjaxHeaders(token);

    dispatch(setIsFetchingTasks(true));

    return axios.get(`${URL}`, AJAX_CONFIG)
        .then((response) => {
            if (!isEmpty(response.data)) {
                response.data.forEach((task) => {
                    dispatch(addTask(task));
                });

                return response.data;
            } else {
                dispatch(setIsFetchingTasks(false));
            }
        })
        .catch((error) => {
            dispatch(setIsFetchingTasks(false));
            errorConsoleDump(error);
            errorHandler(error);
        });
});
Run Code Online (Sandbox Code Playgroud)

减速器.js

export default (state = defaultState, action) => {
    switch (action.type) {
        case ADD_TASK:
        case UPDATE_TASK:
            return update(state, {
                byTaskId: { $merge: action.task },
                isFetching: { $set: false }
            });
        default:
            return state;
    }
};
Run Code Online (Sandbox Code Playgroud)

Dan*_*iel 12

那么在我的回答中,您将学习什么?

  • 使用 Redux 加载常规数据
  • 设置组件生命周期方法,例如 componentDidMount()
  • 从调用动作创建者 componentDidMount()
  • 操作创建者运行代码以发出 API 请求
  • API 响应数据
  • 操作创建者返回一个action带有获取的payload属性数据的

好的,所以我们知道在 Reactjs 应用程序中有两种初始化状态的方法,我们可以调用一个constructor(props)函数,或者我们可以调用组件生命周期方法。在这种情况下,我们在可以假设是基于类的函数中执行组件生命周期方法。

所以而不是这个:

async componentDidMount() {
  await getTasks().then();
}
Run Code Online (Sandbox Code Playgroud)

尝试这个:

componentDidMount() {
  this.props.fetchTasks();
}
Run Code Online (Sandbox Code Playgroud)

所以动作创建者 (fetchTasks()) 状态值变成了组件this.props.fetchTasks();所以我们确实从 中调用动作创建者componentDidMount(),但通常不是你这样做的方式。

异步操作发生在您的操作创建者内部,而不是您的componentDidMount()生命周期方法。componentDidMount()生命周期方法的目的是在启动应用程序时将该操作创建者启动到操作中。

通常,组件通常负责通过调用动作创建者来获取数据,但它是发出 API 请求的动作创建者,所以在那里你有一个异步 JavaScript 操作正在发生,在那里你将要实现ES7 异步/等待语法。

所以换句话说,它不是启动数据获取过程的组件生命周期方法,这取决于操作创建者。组件生命周期方法只是调用启动数据获取过程的动作创建者,也就是异步请求。

需要明确的是,您可以在将动作创建器导入到组件中后this.props.fetchTasks();componentDidMount()生命周期方法中调用,例如导入连接函数,如下所示:

import React from "react";
import { connect } from "react-redux";
import { fetchTasks } from "../actions";
Run Code Online (Sandbox Code Playgroud)

您从未提供您正在执行所有这些操作的组件的名称,但在该文件的底部您需要做 export default connect(null, { fetchTasks })(ComponentName);

我保留第一个参数是null因为您必须通过mapStateToProps,但由于我不知道您是否有任何参数,因此您null现在可以通过。

取而代之的是:

export const getTasks = () => (async (dispatch, getState) => {
    return await axios.get(`${URL}`, AJAX_CONFIG).then();
}
Run Code Online (Sandbox Code Playgroud)

尝试这个:

export const fetchTasks = () => async dispatch => {
  const response = await axios.get(`${URL}`, AJAX_CONFIG);
  dispatch({ type: "FETCH_TASKS", payload: response.data });
};
Run Code Online (Sandbox Code Playgroud)

getState如果您不打算使用它,则无需在您的动作创建器中进行定义。dispatch()在开发异步动作创建器时,您还缺少所需的方法。该dispatch()方法将分派该操作并将其发送到应用程序内的所有不同减速器。

这也是 Redux-Thunk 等中间件发挥作用的地方,因为动作创建者无法开箱即用地处理异步请求。

你没有展示你是如何连接你的 redux-thunk,但它通常在你的根index.js文件中,它看起来像这样:

import React from "react";
import ReactDOM from "react-dom";
import "./index.scss";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";

import App from "./components/App";
import reducers from "./reducers";

const store = createStore(reducers, applyMiddleware(thunk));

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.querySelector("#root")
Run Code Online (Sandbox Code Playgroud)

还记得我说你需要实现的连接函数吗?这是实施的结果,或者您应该实施Provider标签。使用Provider标签,您的组件都可以访问 Redux 存储,但是为了将数据连接到您的组件,您需要导入 connect 函数。

connect 函数是返回到Provider并告诉它想要访问该生命周期方法所在的任何组件内的数据的功能。

如果您已按照我上面的建议更正了所有内容,那么 Redux-Thunk 绝对是您需要实现的。

为什么需要 Redux-Thunk?

它本身没有内置任何东西,它只是一个通用中间件。它所做的一件事是允许我们处理动作创建者,这正是您需要它为您做的事情。

通常,动作创建者返回一个动作对象,但使用 redux-thunk,动作创建者可以返回一个动作对象或一个函数。

如果您返回一个动作对象,它仍然必须具有type您在上面的代码示例中看到的属性,并且它也可以选择具有一个payload属性。

Redux-Thunk 允许你在你的动作创建者中返回一个动作或函数。

但为什么这很重要?谁在乎它返回的是动作对象还是函数?有什么关系?

这又回到了异步 JavaScript 的主题,以及 Redux 中的中间件如何解决 Redux 无法开箱即用地处理异步 JavaScript 的事实。

因此,同步动作创建者会立即返回一个带有准备好的数据的动作。然而,当我们使用异步动作创建器时,比如在这种情况下,它需要一些时间来准备好数据。

因此,任何发出网络请求的动作创建者都有资格作为异步动作创建者。

使用 JavaScript 的网络请求本质上是异步的。

所以 Redux-Thunk,它是一个中间件,它是一个 JavaScript 函数,将在您分派的每个操作中调用。中间件可以阻止操作进行到您的减速器,修改操作等。

  • 这个答案需要更多的爱,惊人的总结,谢谢! (4认同)

dnh*_*yde 0

您进行了设置dispatch(setIsFetchingTasks(true)),但是当 axios 返回时您从未将其设置为 false。dispatch(setIsFetchingTasks(false))之前有没有漏加return response.data;?如果您的 UI 等待 fetchingTasks 完成,这可能是原因