【问题标题】:What is the proper way to getInitialState with remote data in Reactjs?在 Reactjs 中使用远程数据获取初始状态的正确方法是什么?
【发布时间】:2015-06-30 21:16:54
【问题描述】:

问题解决了。代码没问题,问题在于不正确的导入。

这是一篇很长的帖子(由于代码示例)。感谢您的耐心等待,并非常感谢您的帮助!

我们有一个 RoR 后端,前端有 React,我们使用 alt 作为 Flux 的实现。我们也使用 babel 将 ES6 编译为 ES5。所以问题是由于 2 个错误,我无法渲染组件。

第一个是Uncaught TypeError: Cannot read property 'map' of undefined

出现在 MapPalette 组件的渲染函数中:

render() {
  return (
    <div>
      {this.state.featureCategories.map(fc => <PaletteItemsList featureCategory={fc} />)}
    </div>
    );
}

第二个是Uncaught Error: Invariant Violation: receiveComponent(...): Can only update a mounted component.

这是整个 MapPalette 组件

"use strict";
import React from 'react';
import PaletteItemsList from './PaletteItemsList';
import FeatureCategoryStore from '../stores/FeatureTypeStore';

function getAppState() {
  return {
    featureCategories: FeatureCategoryStore.getState().featureCategories
  };
}

var MapPalette = React.createClass({
  displayName: 'MapPalette',

  propTypes: {
    featureSetId: React.PropTypes.number.isRequired
  },

  getInitialState() {
    return getAppState();
  },

  componentDidMount() {
    FeatureCategoryStore.listen(this._onChange);
  },

  componentWillUnmount() {
    FeatureCategoryStore.unlisten(this._onChange);
  },

  render() {
    return (
      <div>
        {this.state.featureCategories.map(fc => <PaletteItemsList featureCategory={fc} />)}
      </div>
      );
  },

  _onChange() {
    this.setState(getAppState());
  }

});

module.exports = MapPalette;

FeatureCategoryStore

var featureCategoryStore = alt.createStore(class FeatureCategoryStore {
  constructor() {
    this.bindActions(FeatureCategoryActions)
    this.featureCategories = [];
  }

  onReceiveAll(featureCategories) {
    this.featureCategories = featureCategories;
  }

})

module.exports = featureCategoryStore

FeatureCategoryActions

class FeatureCategoryActions {
  receiveAll(featureCategories) {
    this.dispatch(featureCategories)
  }

  getFeatureSetCategories(featureSetId) {
    var url = '/feature_categories/nested_feature_types.json';
    var actions = this.actions;
    this.dispatch();

    request.get(url)
           .query({ feature_set_id: featureSetId })
           .end( function(response) {
             actions.receiveAll(response.body);
           });
  }
}

module.exports = alt.createActions(FeatureCategoryActions);

最后 - 我如何渲染 React 组件。

var render = function() {
    FeatureCategoryActions.getFeatureSetCategories(#{ @feature_set.id });
    React.render(
      React.createElement(FeatureSetEditMap, {featureSetId: #{@feature_set.id}}),
      document.getElementById('react-app')
    )
  }

【问题讨论】:

  • 首先,在return块外准备循环,即创建一个新变量,然后将this.state.featureCategories.map(fc =&gt; &lt;PaletteItemsList featureCategory={fc} /&gt;的内容保存在render函数中而不是return块中。然后在返回部分的
    中使用该变量
  • @KoustuvSinha 你为什么要那样做?我认为它现在的阅读方式要好得多。
  • 它读起来更好,是的,但是如果你在返回全部之前构造数组会更容易调试
  • 这是一个很好的观点,即使我不会为了便于调试而构建我的代码。您可能需要编辑评论以反映这一点,因为目前您的评论看起来好像与 OP 问题有关。

标签: reactjs reactjs-flux flux react-alt


【解决方案1】:

首先,你得到的第一个错误:

未捕获的类型错误:无法读取未定义的属性“地图”

是因为你的组件中的this.state是未定义的,这意味着你可能还没有在你的组件中实现getInitialState

您尚未包含您的组件的完整实现,我们需要查看它才能为您提供帮助。但是让我们看一下他们的视图组件示例:

var LocationComponent = React.createClass({
  getInitialState() {
    return locationStore.getState()
  },

  componentDidMount() {
    locationStore.listen(this.onChange)
  },

  componentWillUnmount() {
    locationStore.unlisten(this.onChange)
  },

  onChange() {
    this.setState(this.getInitialState())
  },

  render() {
    return (
      <div>
        <p>
          City {this.state.city}
        </p>
        <p>
          Country {this.state.country}
        </p>
      </div>
    )
  }
})

他们在此处实现getInitialState 以返回商店的当前状态,然后您就可以在render 方法中使用它。在componentDidMount 中,它们侦听来自该存储的更改事件,以便该存储中发生的来自应用程序任何位置的任何事件都将触发重新渲染。 componentWillUnmount 清理事件监听器。非常重要的是不要忘记这一点,否则您的应用程序会泄漏内存!接下来是onChange 方法(可以是任何名称,它不是内部的 React 方法),当发生更改事件时 store 将调用该方法。这只是将组件的状态设置为商店状态。他们在这里再次调用getInitialState 可能有点令人困惑,因为您没有获得初始状态,而是获得了商店的当前状态。

这里的另一个重要注意事项是,此示例无法立即使用 ES6/ES2015 类,因为 React 不再将方法自动绑定到组件的实例。因此,作为类实现的示例将如下所示:

class LocationComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = this.getState();

    this.onChangeListener = () => this.setState(this.getState());
  }

  getState() {
    return locationStore.getState();
  }

  componentDidMount() {
    locationStore.listen(this.onChangeListener);
  }

  componentWillUnmount() {
    locationStore.unlisten(this.onChangeListener)
  }

  render() {
    return (
      <div>
        <p>
          City {this.state.city}
        </p>
        <p>
          Country {this.state.country}
        </p>
      </div>
    );
  }
}

【讨论】:

  • 很好的解释!你能分享一个小提琴吗? :)
  • 感谢您的解释!我已经用组件的完整实现更新了我的问题。我不明白为什么状态未定义。因为从控制台我可以看到这个调用FeatureCategoryActions.getFeatureSetCategories(#{ @feature_set.id }); 发送了一个请求并得到了预期的响应。
  • 尝试在您的 getAppState 函数中添加 console.log(FeatureCategoryStore.getState().featureCategories) 以查看它是否包含您所期望的内容。
  • 那么这就是你得到第一个错误的原因。 console.log(FeatureCategoryStore.getState()) 也会产生 undefined 吗?
  • 不,它显示和反对。 FeatureCategory 对象。我认为它应该是一个包含该对象的数组?
【解决方案2】:

很抱歉浪费了您阅读它的时间,但我想通了,麻烦的原因是我在导入时犯了愚蠢的错误。本质上,我已经导入了另一个名为 required 的 Store。

所以,而不是import FeatureCategoryStore from '../stores/FeatureTypeStore';

应该是import FeatureCategoryStore from '../stores/FeatureCategoryStore';

【讨论】:

    猜你喜欢
    • 2019-06-15
    • 2019-03-23
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2018-01-24
    • 1970-01-01
    • 1970-01-01
    • 2019-03-12
    相关资源
    最近更新 更多