在 ComponentDidMount 中使用 API 调用来更新 Redux 存储会使我的组件呈现两次

IT技术 reactjs redux
2021-04-26 05:34:45

我是 React/Redux 的新手。任何帮助都会很棒。我有一个组件,它在 ComponentDidMount 上调用 API 来获取数据,然后更新我的 Redux 存储。该组件还使用 connect 来获取 Redux 状态并将其作为props传递给一个愚蠢的组件。

componentDidMount() {
  this.props.dispatch(fetchSite()) //this triggers the api call and updates the redux store.
}

return ( 
  <div>
    <Child
        myprop={this.props.name}
    />
  </div>

export default connect((state) => ({name: state.name}))(Container);

因为componentDidMount在组件渲染后触发,会渲染,运行api,然后因为api改变了redux状态而重新渲染。这使得组件渲染两次。第一次没有任何数据,然后第二次有来自 api 的良好数据。

有没有更好的办法?

由于我的 Child 组件根据传递的 props 呈现不同的内容,因此在第一次呈现时会简要显示不同的内容。然后当 api 更新商店并且组件重新渲染时,它再次显示不同的东西。它是两种不同状态的闪烁。

有没有办法让它只用正确的 API 数据渲染一次?

3个回答

让我们想想这里发生了什么。

页面及其所有资产都加载到浏览器中,运行脚本并呈现所有 React 组件。只有在它们被渲染之后,你才能获取你的数据。所以当然会有一个初始状态,然后是一个加载状态。

那么在数据进来之前你如何处理你的应用程序的状态呢?

您已经表达了您不喜欢向应用程序展示空白数据的可理解性。您可以延迟应用程序的呈现,直到数据到达。但这会导致糟糕的用户体验。在发生任何事情之前,他们可能会盯着空白页面几秒钟。

或者有旧的装载机技巧。您可能isLoading: true在初始状态下有类似的东西这将对应于 React 组件中的一些视觉加载指示器(传统上是旋转的 GIF 图像)。如果您必须通过 AJAX 加载数据,这无疑是最佳选择。

更好的方法

但问题是:您不需要为初始状态使用 AJAX。您可以通过将数据附加到页面来完全避免这种延迟。

<!-- place this BEFORE your Redux/React scripts -->
<script>
    // This object must match the shape of your Redux reducer state
    var __INITIAL_DATA__ = {
        todos: [{
            name: "Gather requirements",
            done: false
        }, {
            name: "Write code",
            done: false
        }]
    };
</script>

现在您需要做的就是在创建时“滋润”您的商店。

const store = createStore(rootReducer, window.__INITIAL_DATA__);

更好的方法是让 fetchSite 立即返回一些模拟信息,指示您的组件尚未完成。此外,您应该在 componentWillMount 中分派此操作。如果 fetchSite 返回加载指示符数据,则渲染应该简单地返回 null。当 fetchSite 实际完成时,它会触发 store 更新,组件将在 componentWillMount 中的下一次 fetchSite 调用中获取实际数据。

我这边的另一个例子是,我正在使用 React Lazy 加载我的组件。当我在 redux 中使用异步调度时,在加载的组件中 componentDidMount / useEffect 被多次调用。所以当我删除 React Lazy 时,问题就解决了。