【问题标题】:Asynchronously updating state using reducer from api call使用来自 api 调用的 reducer 异步更新状态
【发布时间】:2019-07-26 23:35:01
【问题描述】:

我正在尝试从 react reducer 中的 api 初始化我的应用程序数据。 api调用,reducer上调用dispatch,但是函数组件重新渲染的时候,依然是初始状态。

以下是相关代码:

Ui.js

import { useReducer } from 'react';
import api from './api';


const reducer = (state: Object, action: {type: string, data: Object}) => {
  switch (action.type) {
    case 'init':
      return action.data;
  }
};

class Ui {
  constructor(schematicId) {
    const initialState = {loadingStatus: 'Loading...'};
    [this.state, this.dispatch] = useReducer(reducer, initialState);

    this.schematicId = schematicId;
    api.init(schematicId).then(data => {
      this.dispatch({type: 'init', data: data});
    });
  }
}

export default Ui;

index.js

let alertStore;
let ui;
const App = props => {
  alertStore = alertStore || new AlertStore();
  ui = ui || new Ui(props.schematicId);

  return (
    <div className="container-fluid">
      {alertStore.alerts.map((a, index) => (
        <Alert dismissible key={index}
          onClose={() => alerts.remove(index)}
          variant={a.variant}>{a.msg}</Alert>
      ))}
      <LoadingBoundary status={ui.state.loadingStatus}>
        ...
      </LoadingBoundary>
    </div>
  );
};

我原来只是有

alertStore = new AlertStore();
ui = new Ui(schematicId);

但它导致网页冻结。我认为 Ui.js 文件中的某些内容在将其更改为之前导致了无限循环

  alertStore = alertStore || new AlertStore();
  ui = ui || new Ui(props.schematicId);

因为我之前在其他应用中使用过alertStore = new AlertStore(); 没有问题。

我在 index.js 和 Ui.js 中设置了断点。 index.js 在 Ui.js 中 switch 语句后重新渲染,但状态仍然是 {loadingStatus: 'Loading...'} 而不是 api 返回的状态。

【问题讨论】:

  • 您能否像 Ui 一样在您的开发控制台中使用类似 useReducer can't be used in non-Function Component 的警告进行确认?
  • Ui 不是 React 组件。 React 没有抱怨,因为调用 useReducer 的类在 App 中被调用,这是一个函数组件。我会在星期一再次检查,但 AlertStore 类几乎与 Ui 相同,只是它没有 api 调用。当我让 AlertStore 在另一个应用程序中工作时发生的所有交互都来自 React 组件内部的调用。

标签: reactjs state react-hooks


【解决方案1】:

在功能组件中进行 api 调用的正确方法是在 useEffect 内部。 useEffect 文档说,

突变、订阅、计时器、日志记录和其他副作用是 不允许在功能组件的主体内部(指 作为 React 的渲染阶段)。这样做会导致令人困惑的错误和 用户界面不一致。

改为使用 useEffect。传递给 useEffect 的函数将运行 在渲染提交到屏幕之后。将效果视为 从 React 的纯函数式世界逃出舱口,进入命令式 世界。

默认情况下,效果会在每次完成渲染后运行,但您可以 选择仅在某些值发生更改时触发它。 [强调添加]

我将 alertStore 和 ui 声明改回原来的样子。

  const alertStore = new AlertStore();
  const ui = new Ui(props.schematicId);

这意味着new AlertStorenew Ui 在每次渲染时都会被调用。没关系,因为 useEffect 可用于确保代码只运行一次,如粗体文档所示。

这是我更新后的带有 useEffect 的 Ui 类

class Ui {
  constructor(schematicId) {
    const initialState = {loadingStatus: 'Loading...'};
    [this.state, this.dispatch] = useReducer(reducer, initialState);
    useEffect(() => {
      api.init(schematicId).then(data => {
        this.dispatch({type: 'init', data: data});
      });
    }, [])

    this.schematicId = schematicId;
  }
}

useEffect 调用的第二个参数 [] 告诉 React 这个函数不依赖于任何组件的 props,所以它应该只被调用一次。

【讨论】:

    猜你喜欢
    • 2019-10-24
    • 2020-03-28
    • 1970-01-01
    • 2020-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    相关资源
    最近更新 更多