使用来自 api 调用的 reducer 异步更新状态
Asynchronously updating state using reducer from api call
我正在尝试从反应减速器中的 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.[=16 返回的状态=]
在功能组件中进行 api 调用的正确方法是在 useEffect 内部。 useEffect 文档说,
Mutations, subscriptions, timers, logging, and other side effects are
not allowed inside the main body of a function component (referred to
as React’s render phase). Doing so will lead to confusing bugs and
inconsistencies in the UI.
Instead, use useEffect. The function passed to useEffect will run
after the render is committed to the screen. Think of effects as an
escape hatch from React’s purely functional world into the imperative
world.
By default, effects run after every completed render, but you can
choose to fire it only when certain values have changed. [emphasis added]
我将 alertStore 和 ui 声明改回原来的样子。
const alertStore = new AlertStore();
const ui = new Ui(props.schematicId);
这意味着 new AlertStore
和 new Ui
在每次渲染时都会被调用。这没关系,因为 useEffect 可用于确保代码只运行一次,如文档中以粗体显示的那样。
这是我使用 useEffect
更新后的 Ui class
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,所以它应该只被调用一次。
我正在尝试从反应减速器中的 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.[=16 返回的状态=]
在功能组件中进行 api 调用的正确方法是在 useEffect 内部。 useEffect 文档说,
Mutations, subscriptions, timers, logging, and other side effects are not allowed inside the main body of a function component (referred to as React’s render phase). Doing so will lead to confusing bugs and inconsistencies in the UI.
Instead, use useEffect. The function passed to useEffect will run after the render is committed to the screen. Think of effects as an escape hatch from React’s purely functional world into the imperative world.
By default, effects run after every completed render, but you can choose to fire it only when certain values have changed. [emphasis added]
我将 alertStore 和 ui 声明改回原来的样子。
const alertStore = new AlertStore();
const ui = new Ui(props.schematicId);
这意味着 new AlertStore
和 new Ui
在每次渲染时都会被调用。这没关系,因为 useEffect 可用于确保代码只运行一次,如文档中以粗体显示的那样。
这是我使用 useEffect
更新后的 Ui classclass 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,所以它应该只被调用一次。